简介:
大家好,我是xp。
突然想起,上篇的MySQL5.7 MVCC原理分析与调试只是描述了mvcc解决不可重复读的情况,并没有描述如何解决幻读的。
幻读:侧重于insert、delete这种操作,第一次查出生成视图之后,即便有别的事务insert、delete,也不影响后续的查询。
首先解释下本次涉及的3种锁:
着重强调一下,所有锁,都是针对索引的,锁的是索引,记住。如果没有索引,那就芭比Q了。
B+tree:非叶子节点只存储索引,所有叶子节点之间都有一个链指针,不存储数据,数据记录都存放在叶子节点中。
如下图所示:
上面是B+tree的数据结构,一定要记住叶子节点,里面包含了索引和data(主键/其他数据)。
有一点需要说明:叶子节点是按页存储的,页之间是双向链表,页里面的叶子节点是单向链表。
来个预热:
1:表结构(id是主键,count为普通索引):
开启事务1:
先查询count = 5,生成视图。
开始事务2:
此时插入一条count = 4的数据,很明显,被阻塞住了。
为啥要阻塞count = 4的记录呢?因为RR级别下,其实是加了GAP锁,不允许插入某个范围的值,从而避免了幻读的出现,那么这个范围是怎么定义的呢?
这是官网的,应用于我上面的例子,锁住的就是(3,5],(5,7],左开右闭。
好了,大家都散了吧。结论已经得出了:
加了范围锁,不允许插入数据,所以不会出现幻读的情况,范围如上。
什么,你反对?
对于GAP的范围,看到网上很多讨论,有的说是测试的左开右闭,也有说测试的左闭右开。
这里,我想说,虽然实践是检验真理的标准,但是实践的前提是,你得明白实践方式对不对。
好,我们继续,上面的测试情况:
可以具体成上图,因为是普通索引,所以data只包含了主键。
那么锁住的是哪一块呢?
锁的其实是上述的区间,而左开右闭,左闭右开都是错误的。要看插入的位置
超出那个区间的就可以插入,反之即不可以插入,这个位置是按主键排序的。
我们可以验证一下:
1.count=3,id=32:
插入成功,没有阻塞。
2.count=3,id=34:
插入失败,阻塞。
3.count=7,id=76:
插入失败,阻塞。
4.count=7,id=78:
插入成功,没有阻塞。
验证GAP:
这下是真的结束了,大家可以散了。
什么?还没走?那么来传授一个面试装13技巧:
如果是更新语句,尽量以主键为条件,唯一约束也可以,因为主键和唯一约束都只会把更新的那一行加X锁。再不济,也要是以普通索引去更新。
大忌:以非索引字段去更新,这样会导致所有的列都加X锁,此时所有的 insert/update 都无法操作。
验证(count已经改为非索引):
可以看到,阻塞住了。
查询INNODB_LOCKS:
supremum pseudo-record:表示无穷大