不可重复读是指在一个事务内多次读取同一数据集合。在这个事务还没有结束时,另外一个事务也访问该同一数据集合,并做了一些DML操作。因此,在第一个事务中的两次读数据之间,由于第二个事务的修改,那么第一个事务两次读到的数据可能是不一样的。这样就发生了在一个事务内两次读到的数据是不一样的情况,这种情况称为不
可重复读。
不可重复读和脏读的区别是:脏读是读到未提交的数据,而不可重复读读到的却是已经提交的数据,但是其违反了数据库事务一致性的要求。可以通过下面一个例子来观察不可重复读的情况。
以下例子是在READ COMMITTED隔离级别下进行的
首先设置当前两个会话的事务隔离级别为READ COMMITTED
set session transaction isolation level read committed;
时间点 | 会话A | 会话B |
---|---|---|
时间点1 | begin; | |
时间点2 | begin; | |
时间点3 | select * from t; | |
时间点4 | insert into t values(2,2); | |
时间点5 | select * from t; | |
时间点6 | commit; | |
时间点7 | select * from t; | |
时间点7 | commit; |
分析如下:
在会话A中开始一个事务,第一次进行查询读取到的数据只有三条。
然后在会话B中开始了另一个事务,插入了一条为id=2,code=2的数据,在没有进行提交之前,对会话A进行了一次读取,读取到的数据也是只有三条,证明没有发生脏读的现象。但是当会话B的事务提交了之后,在会话A中再次进行读取的时候,可以发现数据有四条了。
总结:
一般来说,不可重复读的问题是可以接受的,因为其读到的是已经提交的数据,本身并不会带来很大的问题。因此,很多数据库厂商(如Oracle、Microsoft SQL Server)将其数据库事务的默认隔离级别设置为READ COMMITTED,在这种隔离级别下允许不可重复读的现象。
在InnoDB存储引擎中,通过使用Next-Key Lock算法来避免不可重复读的问题。在MySQL官方文档中将不可重复读的问题定义为Phantom Problem,即幻像问题。在Next-Key Lock算法下,对于索引的扫描,不仅是锁住扫描到的索引,而且还锁住这些索引覆盖的范围(gp)。因此在这个范围内的插人都是不允许的。这样就避免了另外的事务在这个范围内插入数据导致的不可重复读的问题。因此,nnoDB存储引擎的默认事务隔离级别是READ REPEATABLE,采用Next-Key Lock算法,避免了不可重复读的现象。
特别鸣谢:MYSQL技术内幕INNODB存储引擎【机械工业出版社】