1、事务
(1)事务的概念
事务由单独单元的一个或多个SQL语句组成,在这 个单元中,每个MySQL语句是相互依赖的。而整个单独单 元作为一个不可分割的整体,如果单元中某条SQL语句一 旦执行失败或产生错误,整个单元将会回滚。所有受到影 响的数据将返回到事物开始以前的状态;如果单元中的所 有SQL语句均执行成功,则事物被顺利执行。
(2)事务的acid属性
a. 原子性(Atomicity) 原子性是指事务是一个不可分割的工作单位,事务中的操作要么 都发生,要么都不发生。
b. 一致性(Consistency) 事务必须使数据库从一个一致性状态变换到另外一个一致性状态 。
c. 隔离性(Isolation) 事务的隔离性是指一个事务的执行不能被其他事务干扰,即一个 事务内部的操作及使用的数据对并发的其他事务是隔离的,并发 执行的各个事务之间不能互相干扰。
d. 持久性(Durability) 持久性是指一个事务一旦被提交,它对数据库中数据的改变就是 永久性的,接下来的其他操作和数据库故障不应该对其有任何影 响
(3)事务的创建步骤
步骤一:开启事务
set autocommit = 0; #在开启事务前需要关闭自动提交,0代表off,1代表on,当此语句执行时默认开启事务,因此开启事务语句可选则执行
start transaction; #可选
步骤二:编写事务中的sql语句(select 、update、delete、insert)
步骤三:结束事务
commit; #提交
rollback #回滚
2、没有隔离机制会产生的问题
对于同时运行的多个事务, 当这些事务访问数据库中相同的数据时, 如果没 有采取必要的隔离机制, 就会导致各种并发问题,如脏读、不可重复读、幻读。
其中涉及到的相关命令如下:
#查看隔离级别
select @@tx_isolation;
#设置隔离级别
set session transcarion isolation level 隔离级别;
(1)脏读
对于两个事务T1和T2,如果T1对数据进行了修改,但还没有提交或者回滚(相当于撤销之前的修改)时,T2读取该数据,此时T2读取到的是已经修改后的数据。当T1进行回滚后,T2再次读取数据,此时数据为原数据(修改前的数据)。
示例:
现有学生表如下所示。(注意:每个小节提到的原始表就是指此时的学生表)
当教师a通过事务t1将学号为1的学生姓名修改为“john”,并没有提交或者回滚时,教师b通过事务t2将查看学生表stu,此时其看到的是下面的数据:
|
Id |
name |
|
1 |
john |
|
2 |
李四 |
然后教师a撤回了其修改的操作(回滚)。此时,教师b再次通过事务t2,查看学生表stu,其看到的数据为:
|
Id |
name |
|
1 |
张三 |
|
2 |
李四 |
因此,教师a修改数据后且没有提交或回滚时,教师b能够查看到的数据是修改后的数据,而当教师a回滚后,教师b再次通过相同事务查询到的数据时原数据的这种现象就称为脏读。
(2)不可重复读
在同一事务中多次查询相同数据,然而查询结果不一致,这种现象称为不可重复读。
例如,在上述示例中,教师b在同一个事务中查询学生表,第一次为修改后的数据,第二次为原始数据,两次查询的结果不一致,因此存在不可重复读的现象。
(3)幻读
对事务T1和T2,T1读取表中数据为n行,T2向表中新插入m行数据,此时T1再次读取表中数据为n+m行,这种现象就称为幻读。
例如上述示例,教师a在事务t1中将表中所有姓名全部换成“john”(update stu set name = ‘john’;),将是2行数据受到影响(没有提交或回滚),此时教师b在事务t2中插入一行新数据并提交,这个时候教师a再次将表中姓名全部换成‘lucy‘,此时将是3行数据受到影响。
3、隔离级别
|
隔离级别 |
描述 |
|
read uncommitted (读取未提交数据) |
允许事务读取被其他事务变更,这个级别脏读、不可重复读、幻读等问题都会出现。 |
|
read committed (读取提交数据) |
只允许事务读取被其他事务提交的变更,该级别可以避免脏读问题,但是不能避免不可重复读和幻读的问题。 |
|
repeatable read (可重复读) |
确保事务可以多次从一个字段中读取相同的值,在这个事务持续期间,不允许其他事务对这个字段进行变更,可以避免脏读、不可重复读问题,不能避免幻读的问题。 |
|
serializable (串行化) |
确保事务可以从一个表中读取相同的行。在这个事务持续期间,禁止其他事务对这个表进行插入、删除、更新等操作,所有并发问题都可以解决,但是性能十分低下。 |
下面将分别介绍设置隔离级别为read committed、read committed、serializable后产生的效果。
注意下面各个示例中提到的事务t1和t2都是提前设置好相同隔离级别的前提下进行的操作。
(1)read committed
设置隔离级别为read committed后,如上述示例。当教师a通过事务t1将学号为1的学生姓名修改为“john”,并没有提交或者回滚时,教师b通过事务t2将查看学生表stu,此时其看到的是没有修改的数据:
|
Id |
name |
|
1 |
张三 |
|
2 |
李四 |
当教师a通过事务t1修改数据并提交后,教师b再次通过事务t2将查看学生表stu,此时其看到的是已经修改的数据,进而避免了脏读。
|
Id |
name |
|
1 |
john |
|
2 |
李四 |
(2)repeatable read
设置隔离级别为repeatable read后,如上述示例。教师a通过事务t1修改原始stu表中数据(如将张三改成王五)且没有提交时,教师b通过事务t2查看学生表stu,此时其看到的数据如下:
|
Id |
name |
|
1 |
张三 |
|
2 |
李四 |
当教师a结束事务t1后(即提交了),教师b再次通过事务t2查看学生表stu,此时其看到的数据仍然不变,具体如下:
|
Id |
name |
|
1 |
张三 |
|
2 |
李四 |
然而,当教师b也结束事务t2(即提交了)后,其再次查看学生表stu时,其看到的是已经修改后的数据,即:
|
Id |
name |
|
1 |
王五 |
|
2 |
李四 |
(3)serializable
设置隔离级别为repeatable read后,如上述示例。教师a通过事务t1对将学生表stu(原始表共有两行数据)中所有的姓名全部替换成“unknown”,在教师a还未执行修改操作时,教师b通过事务t2插入一行新的数据,并执行,此时发现教师b的操作无法执行,一直处于等待的状态,而当教师a的修改操作执行并结束事务t1(即提交了)之后时,教师b的插入操作才能执行。
教师a的操作:输入update后未执行。
教师b的操作:在教师a未执行update时,教师b的insert操作一直等待无法执行。
当教师a执行update,并提交后,教师b的操作方可执行,如下图所示。
教师a的操作:
由于教师b的插入未执行,所以只有两行受影响,因此避免了幻读的问题。
教师b的操作:
此时教师b查看stu看到的数据为