欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

InnoDB的RR隔离级别能否防止“幻读”

程序员文章站 2022-06-14 16:37:18
...

一、快照读和当前读(参考这篇文章

在RR级别中,通过MVCC机制,虽然让数据变得可重复读,但我们读到的数据可能是历史数据,不是数据库最新的数据。这种读取历史数据的方式,我们叫它快照读 (snapshot read),而读取数据库最新版本数据的方式,叫当前读 (current read),这两者是冲突的。

1.快照读
当执行select操作时,Innodb默认执行快照读,会记录下这次select后的结果,之后select 的时候就会返回这次快照的数据,即使其他事务提交了不会影响当前select的数据,这就实现了可重复读了(InnoDB默认隔离级别是可重复读,和这个说法一致)。快照的生成时机是在第一次执行select的时候,也就是说假设当A开启了事务,然后没有执行任何操作,这时候B insert了一条数据然后commit,这时候A执行 select,读到的数据中就会有B添加的那条数据。之后无论再有其他事务commit都没有关系,因为快照已经生成了,后面的select都是根据快照来的。

先提一句:快照读是通过MVCC和undo log实现的,暂不展开

2.当前读
对于会修改数据的操作(update、insert、delete)都是采用当前读的模式。在执行这几个操作时会读取最新的记录,即使是别的事务提交的数据也可以查询到。假设要update一条记录,但是在另一个事务中已经delete掉这条数据并且commit了,如果update就会产生冲突,所以在update的时候需要知道最新的数据(例子可参考这篇文章的“试验一”)

那么如何使用select操作实现当前读呢?答案是需要手动加锁(例子可参考这篇文章的“试验四”)

select * from table where ? lock in share mode;
select * from table where ? for update;

二、select for update 和 select lock in share mode

select…lock in share在select的结果集上加一个共享锁.允许其他会话读取这些数据,但不允许修改.
select…for update 将读取的结果集加一个排他锁. 阻止其他会话读或写该结果集.
而这里加的锁是next-key lock

三、当前读时如何避免幻读
当前读是通过next-key lock实现,next-key lock会锁住一个范围,并且锁定记录本身,使得其他事务不能操作锁定范围内的记录,也就杜绝了出现“幻影”。(因为其他事务根本无法在此范围里插入数据)

四、快照读时如何避免幻读
呵呵,快照读时没有避免幻读,只是在select时都是读到历史数据,看起来是没有幻读,但是当你更新时,发现,明明没有这条数据呀?!(例子可参考中的“试验一”和“试验二”)

五、结论
在InnoDB的RR隔离级别下
1.对于快照读,通过MVCC实现了重复读,但是没有完全避免幻读,只是在简单select时避免了幻读
2.要完全避免幻读,需要手动加锁进行当前读,这时会使用next-key lock避免幻读