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

简介:
大家好 , 我是xp 。
突然想起 , 上篇的MySQL5.7 MVCC原理分析与调试只是描述了mvcc解决不可重复读的情况 , 并没有描述如何解决幻读的 。
幻读:侧重于insert、delete这种操作 , 第一次查出生成视图之后 , 即便有别的事务insert、delete , 也不影响后续的查询 。
首先解释下本次涉及的3种锁:

  1. X锁:俗称写锁/排他锁 , 即加锁之后 , 不允许别的事务来修改当前数据 。
  2. GAP锁:俗称间隙锁 , 就是锁住某个范围(RR级别解决幻读的关键)
  3. Next-Key锁:GAP锁+记录本身
着重强调一下 , 所有锁 , 都是针对索引的 , 锁的是索引 , 记住 。如果没有索引 , 那就芭比Q了 。
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
八股文:B+tree:非叶子节点只存储索引 , 所有叶子节点之间都有一个链指针 , 不存储数据 , 数据记录都存放在叶子节点中 。
如下图所示:
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
上面是B+tree的数据结构 , 一定要记住叶子节点 , 里面包含了索引和data(主键/其他数据) 。
有一点需要说明:叶子节点是按页存储的 , 页之间是双向链表 , 页里面的叶子节点是单向链表 。
来个预热:
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
场景:1:表结构(id是主键 , count为普通索引):
 
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
 
开启事务1:
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
先查询count = 5 , 生成视图 。
开始事务2:
 
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
【Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理】此时插入一条count = 4的数据 , 很明显 , 被阻塞住了 。
为啥要阻塞count = 4的记录呢?因为RR级别下 , 其实是加了GAP锁 , 不允许插入某个范围的值 , 从而避免了幻读的出现 , 那么这个范围是怎么定义的呢?
 
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
这是官网的 , 应用于我上面的例子 , 锁住的就是(3 , 5] , (5 , 7] , 左开右闭 。
好了 , 大家都散了吧 。结论已经得出了:
加了范围锁 , 不允许插入数据 , 所以不会出现幻读的情况 , 范围如上 。
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
什么 , 你反对?
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
对于GAP的范围 , 看到网上很多讨论 , 有的说是测试的左开右闭 , 也有说测试的左闭右开 。
这里 , 我想说 , 虽然实践是检验真理的标准 , 但是实践的前提是 , 你得明白实践方式对不对 。
好 , 我们继续 , 上面的测试情况:
可以具体成上图 , 因为是普通索引 , 所以data只包含了主键 。
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
那么锁住的是哪一块呢?
 
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
锁的其实是上述的区间 , 而左开右闭 , 左闭右开都是错误的 。要看插入的位置
 
Next-Key锁的分析与调试 MySQL5.7 解决幻读的原理

文章插图
 
超出那个区间的就可以插入 , 反之即不可以插入 , 这个位置是按主键排序的 。
  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:


推荐阅读