欢迎重视MySQL专栏MySQL历险记
强烈建议保藏本导航文【MySQL历险记】MySQL的中心特性汇总

前言

SQL规范中界说了4种阻隔等级,分别是读未提交、读已提交、可重复读以及序列化。不同的阻隔等级下,能够处理不同的并发问题,如下图所示。当然MySQL也基本遵循了这个规范,可是在完成上稍有不同。

本文要点探讨下MySQL是怎么处理幻读问题的,首要串行化阻隔等级铁定是能够处理所有的并发问题,相当于每个业务按顺序执行,可是功能很差,不是本文要点评论对象。实际上MySQL默许的业务阻隔等级是可重复读,莫非这种阻隔等级下MySQL就不管幻读问题了吗?其实不是的,本文就带大家一同看看MySQL在可重复读阻隔等级下是怎么处理幻读问题的。

你知道MySQL是如何解决幻读的吗?

什么是幻读?

幻读是指一个业务中依照某个条件先后两次读取数据库,两次读取成果的条数不同,愈加强调的是读到了之前没有读到的数据,这种现象称为幻读

举个比方:

你知道MySQL是如何解决幻读的吗?

  1. 业务A一开始只读取到‘张三’的数据记载。
  2. 然后另外一个业务B插入了‘赵六’的数据记载。
  3. 业务A再次读取,发现了‘张三’、‘赵六’两条数据,分明同一个业务,相同的查询条件,前后两次读取,多了一条记载,相当于“幻影”,这种状况便是幻读。

什么是普通读和当时读?

其实读这个操作也有两种状况,一种是普通读,就像上面比方的那样,还有一种便是当时读。不同的读形式,MySQL在可重复读阻隔等级下的完成方法也是不一样的。

普通读

普通读又名快照读,也便是利用MVCC机制读取快照中的数据。不加锁的简略的SELECT 都归于快照读,比方这样:

SELECT * FROM user WHERE ...
  • 快照读是根据MVCC完成的,提高了并发的功能,降低开销
  • 大部分业务代码中的读取都归于快照读

当时读

当时读读取的是记载的最新版别,读取时会对读取的记载进行加锁, 其他业务就有可能堵塞。加锁的 SELECT,或者对数据进行增修改都会进行当时读。比方:

SELECT * FROM user LOCK IN SHARE MODE; # 同享锁
SELECT * FROM user FOR UPDATE; # 排他锁
INSERT INTO user values ... # 排他锁
DELETE FROM user WHERE ... # 排他锁
UPDATE user SET ... # 排他锁
  • update、delete、insert句子尽管没有select, 可是它们也会先进行读取,而且只能读取最新版别。

那不同的读形式下,MySQL分别是怎么防止幻读的呢?请接着往下看。

普通读是怎么防止幻读的?

MySQL在可重复读阻隔等级下,是经过MVCC机制防止幻读的。

MVCC机制,能够简略理解成在业务发动的时分对数据库拍了个“快照”,它保留了那个时刻数据库的数据状况,那么这个业务后续的读取都能够从这个“快照”中获取,哪怕其他业务新加了数据,也不会影响到“快照”中的数据,也就不会呈现幻读了。

关于MVCC的详细机制强烈你阅览本文看完这篇还不懂MySQL的MVCC机制算我输

你知道MySQL是如何解决幻读的吗?

  • 业务A在发动的时分创建了一个“快照”,查询出成果“小红,小蓝”
  • 后续业务B插入一条记载“小飞”,提交
  • 然后业务A再次相同查询条件查询,它会运用“快照”读取,所以仍是“小红,小蓝”

小结: 针对快照读(普通 select 句子),是经过 MVCC 方法处理了幻读。

当时读是怎么防止幻读的?

普通读(快照读)实际上读取的是前史版别中的数据,但一向用这种方法读取在某些场景下是有问题的。

假设你要 update 一个记载,可是另一个业务已经 delete 这条记载而且提交业务了,这样不是会产生抵触吗,所以 update 的时分肯定要知道最新的数据。也便是要做当时读

那么针对当时读,MySQL在可重复读阻隔等级下是怎么防止幻读的呢?

也便是说不能读取“快照”了,因为你要最新状况的数据,那么能不能在当时读的时分,对这段区间都加上锁,让别的业务堵塞,无法插入。因此,MySQLInnoDB引擎为了处理可重复读阻隔等级运用当时读而造成的幻读问题,引入了空隙锁

你知道MySQL是如何解决幻读的吗?

表中有一个规模 id 为(3,5)空隙锁,那么其他业务就无法插入 id = 4 这条记载了,这样就有用的防止幻读现象的产生。

举个比方:

你知道MySQL是如何解决幻读的吗?

  • 业务A的for_update是归于当时读,它会对确定 id 规模 (2, +∞] ,相当于理解是空隙锁。
  • 业务B插入了id=5的数据,(2, +∞]规模被确定了,所以无法插入,堵塞。
  • 经过这种加锁堵塞的方法,也能够防止幻读。

小结: 针对当时读(select … for update 等句子),是经过 next-key lock(记载锁+空隙锁)方法处理了幻读。

关于MySQL锁的更多内容检查盘点MySQL中的各种锁

总结

MySQL默许采用的阻隔等级是可重复读,在这种阻隔等级下不同的读形式,针对幻读问题采用了不同处理方案:

  • 针对快照读(普通 select 句子),是经过 MVCC 方法处理了幻读。
  • 针对当时读(select ... for update 等句子),是经过 next-key lock(记载锁+空隙锁)方法处理了幻读。

可是,强调一点的是,MySQL在可重复读等级下,并没有完完全全的处理幻读问题,特别是在一个业务的快照读和当时读穿插运用的场景下,仍是会呈现幻读的状况,比方下图所示。

你知道MySQL是如何解决幻读的吗?

假如本文对你有协助的话,请留下一个赞吧

本文正在参与「金石计划 . 瓜分6万现金大奖」