MySQL - 幻读

2019/10/13

What is Phantom Read?

这几天在网上看了大量相关文章,关于是否会在Repeatable Read事务隔离等级下产生幻读问题,众说纷纭。之前只是仿照他人写了个小例子,并没有深入了解,因此决定单独写篇文章,结合官方文档以及其他博客重新梳理下。

首先第一个问题就是,什么是Phantom Read?

The so-called phantom problem occurs within a transaction when the same query produces different sets of rows at different times.

也就是说,在一个事务内部,相同的查询语句,执行多次的返回结果不同。

这里需要补充两点,
第一,我们假设在Repeatable Read事务隔离等级下。
第二,上面提到查询语句是普通查询Plain Query,也就是Non-locking Read


下面我们来看个例子:


Example

首先,看第五步,在Session2中往user表中插入一条新的数据。
然后,回到Session1中的第六步,并没有查询出新插入的数据,因为在RR隔离等级下,使用的是Consistent Read
接下来,第七步,Session2提交事务。
第八步,Session1仍没有查询到新插入的数据。
最后,第九步,在Session1中更新一条看似不存在,但实际在Session2 中新插入的数据,然后相同的查询语句,这次查询到了新的数据,此时使用的是Current Read


How to avoid that?

那么如何避免Phantom Read?

第一种方式: Locking Read

Phantom Read是因为在同一个事务中多次执行相同的查询语句,返回的结果不同,也就是说会返回其他事务新插入的数据。

那么在并发的场景下,想办法阻止其他事务插入新数据不就行了。

在第三步中对user表进行Locking Read,因此在第四步,当Session2试图往user表中插入一条新数据时,被阻塞waiting


第二种方式: 使用Serializable隔离等级,不过在高并发场景下,性能太差,不建议使用。

可以看到,在Serializable隔离等级下,可以阻止Phantom Read的关键在于,一旦在其他事务中插入新的数据,回到当前事务中你永远也无法读到。
因为在当前情况下,你只能Consistent Read,也就是读取开启当前事务时的查询快照SnapShot,一旦使用Current Read,比如Locking Read - Select for share, update,或者DML(Data Manipulation Language) - Insert, Update, Delete就会被阻塞。


Reference


一位喜欢提问、尝试的程序员

(转载本站文章请注明作者和出处 姚屹晨-yaoyichen

Post Directory