<返回更多

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

2022-03-02    金色的雨16
加入收藏

简介:

大家好,我是xp。

突然想起,上篇的MySQL5.7 MVCC原理分析与调试只是描述了mvcc解决不可重复读的情况,并没有描述如何解决幻读的。

幻读:侧重于insert、delete这种操作,第一次查出生成视图之后,即便有别的事务insert、delete,也不影响后续的查询。

首先解释下本次涉及的3种锁:

  1. X锁:俗称写锁/排他锁,即加锁之后,不允许别的事务来修改当前数据。
  2. GAP锁:俗称间隙锁,就是锁住某个范围(RR级别解决幻读的关键)
  3. Next-Key锁:GAP锁+记录本身

着重强调一下,所有锁,都是针对索引的,锁的是索引,记住。如果没有索引,那就芭比Q了。

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

八股文:

B+tree:非叶子节点只存储索引,所有叶子节点之间都有一个链指针,不存储数据,数据记录都存放在叶子节点中。

如下图所示:

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

上面是B+tree的数据结构,一定要记住叶子节点,里面包含了索引和data(主键/其他数据)

有一点需要说明:叶子节点是按页存储的,页之间是双向链表,页里面的叶子节点是单向链表。

来个预热:

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

场景:

1:表结构(id是主键,count为普通索引):

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

 

开启事务1:

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

先查询count = 5,生成视图。

开始事务2:

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

此时插入一条count = 4的数据,很明显,被阻塞住了。

为啥要阻塞count = 4的记录呢?因为RR级别下,其实是加了GAP锁,不允许插入某个范围的值,从而避免了幻读的出现,那么这个范围是怎么定义的呢?

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

这是官网的,应用于我上面的例子,锁住的就是(3,5],(5,7],左开右闭。

好了,大家都散了吧。结论已经得出了:

加了范围锁,不允许插入数据,所以不会出现幻读的情况,范围如上。

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

什么,你反对?

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

对于GAP的范围,看到网上很多讨论,有的说是测试的左开右闭,也有说测试的左闭右开。

这里,我想说,虽然实践是检验真理的标准,但是实践的前提是,你得明白实践方式对不对。

好,我们继续,上面的测试情况:

可以具体成上图,因为是普通索引,所以data只包含了主键。

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

那么锁住的是哪一块呢?

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

锁的其实是上述的区间,而左开右闭,左闭右开都是错误的。要看插入的位置

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

超出那个区间的就可以插入,反之即不可以插入,这个位置是按主键排序的。

  1. count=3,id=32,可以插入
  2. count=3,id=34,不可以插入
  3. count=7,id=76,不可以插入
  4. count=7,id=78,可以插入

我们可以验证一下:

1.count=3,id=32:

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

插入成功,没有阻塞。

2.count=3,id=34:

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

 

插入失败,阻塞。

3.count=7,id=76:

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

插入失败,阻塞。

4.count=7,id=78:

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

插入成功,没有阻塞。

验证GAP

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

 

这下是真的结束了,大家可以散了。

什么?还没走?那么来传授一个面试装13技巧:

如果是更新语句,尽量以主键为条件,唯一约束也可以,因为主键和唯一约束都只会把更新的那一行加X锁。再不济,也要是以普通索引去更新。

大忌:以非索引字段去更新,这样会导致所有的列都加X锁,此时所有的 insert/update 都无法操作。

验证(count已经改为非索引):

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

可以看到,阻塞住了。

查询INNODB_LOCKS:

 

MySQL5.7 解决幻读的原理(Next-Key锁的分析与调试)

 

supremum pseudo-record:表示无穷大

声明:本站部分内容来自互联网,如有版权侵犯或其他问题请与我们联系,我们将立即删除或处理。
▍相关推荐
更多资讯 >>>