本章着重点是介绍:出现脏读、幻读、不可重复读的原因,以及设定四种隔离级别如何解决这些现象,结合图例让读者阅读得更加通透
所谓事务是用户定义的一个数据库序列,这些操作要么全做,要么全不做,是一个不可分割的工作单位。
在SQL中,定义事务的语句一般有三条:
BEGIN TRANSACTION;
COMMIT;
ROLLBACK;
事务通常是以BEGIN TRANSACTION开始,以COMMIT或ROLLBACK结束。
【重点】事务具有4个特性:原子性(Atomicity)、一致性(Consistency)、隔离性(Isolation)和持续性(Durability)。这4个特性简称为ACID特性
事务是一个原子性质的操作单元,事务里面的对数据库的操作要么都执行,要么都不执行。
在事务开始之前和完成之后,数据都必须保持一致状态,必须保证数据库的完整性。也就是说,数据必须符合数据库的规则。
一个事务的执行不能被其他事务干扰。即一个事务的内部操作及使用的数据对其他并发事务是隔离的,并发执行的各个事务之间不能互相干扰。
持久性也成为永久性,指一个事务一旦提交,它对数据库中数据的改变就应该是永久的。
(1)脏读
脏读也俗称“读未提交”,顾名思义,就是某一事务A读取到了事务B未提交的数据,如图所示:
T2时刻,事务B把原来李华的年龄由原数据13改为了23,此后又被T3时刻的事务A读取到了,但是T4时刻事务B发生异常,进行了回滚操作。这个过程,我们称23为脏数据,事务A进行了一次脏读。
(2)不可重复读
不可重复读,有时候也会说成“读已提交”。什么意思呢,就是在一个事务内,多次读取同一个数据,却返回了不同的结果。实际上,这是因为在该事务间隔读取数据的期间,有其他事务对这段数据进行了修改,并且已经提交,就会发生不可重复读事故。
图示中事务A在T1和T4查询同一语句,却得到了不同的结果,这是因为T2~T3时刻事务B对该数据进行了修改,并提交。这个过程,出现了在一个事务内两次读到的数据却是不一样的,我们称为是不可重复读。
不可重复读和脏读的区别:前者是“读已提交”,后者是“读未提交”
(3)幻读
幻读是指当事务不独立执行时,插入或者删除另一个事务当前影响的数据而发生的一种类似幻觉的现象。举个例子,某事务在检查表中的数据数count时,是10,过一段时间之后再查是11,这就发生了幻读,之前的检测获取到的数据如同幻觉一样。
出现幻读和不可重复读的原因很像,都是在多次操作数据的时候发现结果和原来的不一样了,出现了其他事务干扰的现象。但是,幻读的偏重点是添加和删除数据,多次操作数据得到的记录数不一样;不可重复读的偏重点是修改数据,多次读取数据发现数据的值不一样了。幻读图示如下:
事务B向表中新插入了一条数据,事务A在T3时刻后查询数据的时候,突然发现数据和以前查询的时候多出了一项,像产生了幻觉一样。
1.读未提交(Read Uncommitted):在该隔离级别,所有事务都可以看到其他未提交事务的执行结果。本隔离级别很少用于实际应用,因为它的性能也不比其他级别好多少。读取未提交的数据,也被称之为脏读(Dirty Read)
2.读已提交(Read Committed):这是大多数数据库系统的默认隔离级别(但不是MySQL默认的)。它满足了隔离的简单定义:一个事务只能看见已经提交事务所做的改变。这种隔离级别也支持所谓的不可重复读(NonrepeatableRead),因为同一事务的其他实例在该实例处理其间可能会有新的commit,所以同一select可能返回不同结果
3.可重复读(Repeatable Read):这是MySQL的默认事务隔离级别,同一事务的多个实例在并发读取数据时,会看到同样的数据。不过理论上,这会导致另一个棘手的问题:幻读(Phantom Read)。简单的说,幻读指当用户读取某一范围的数据行时,另一个事务又在该范围内插入了新行,当用户再读取该范围的数据行时,会发现有新的“幻影” 行。
4.可串行化(Serializable):这是最高的隔离级别,它通过强制事务排序,使之不可能相互冲突,从而解决幻读问题。简言之,它是在每个读的数据行上加上共享锁。在这个级别,可能导致大量的超时现象和锁竞争
这四种隔离级别越往后越影响性能,如何选取根据业务需求而定。以下是四种隔离级别中对脏读、不可重复读、幻读的影响情况。