把MySQL中的各种锁及其原理都画出来( 五 )


聚簇索引 , 查询命中聚簇索引就是 InnoDB 存储引擎下的主键索引 , 具体可参考《MySQL索引》 。
下图展示了使用 UPDATE book SET score = 9.2 WHERE ID = 10 语句命中的情况下在 RC 和 RR 隔离等级下的加锁 , 两种隔离等级下没有任何区别 , 都是对 ID = 10 这个索引加排他记录锁 。

把MySQL中的各种锁及其原理都画出来

文章插图
 
聚簇索引 , 查询未命中下图展示了 UPDATE book SET score = 9.2 WHERE ID = 16 语句未命中时 RR 隔离级别下的加锁情况 。
在 RC 隔离等级下 , 不需要加锁;而在 RR 隔离级别会在 ID = 16 前后两个索引之间加上间隙锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
值得注意的是 , 间隙锁和间隙锁之间是互不冲突的 , 间隙锁唯一的作用就是为了防止其他事务的插入新行 , 导致幻读 , 所以加间隙 S 锁和加间隙 X 锁没有任何区别 。
二级唯一索引 , 查询命中下图展示了 UPDATE book SET score = 9.2 WHERE ISBN = 'N0003' 在 RC 和 RR 隔离等级下命中时的加锁情况 。
在 InnoDB 存储引擎中 , 二级索引的叶子节点保存着主键索引的值 , 然后再拿主键索引去获取真正的数据行 , 所以在这种情况下 , 二级索引和主键索引都会加排他记录锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
二级唯一索引 , 查询未命中下图展示了 UPDATE book SET score = 9.2 WHERE ISBN = 'N0008' 语句在 RR 隔离等级下未命中时的加锁情况 , RC 隔离等级下该语句未命中不会加锁 。
因为 N0008 大于 N0007 , 所以要锁住 (N0007,正无穷)这段区间 , 而 InnoDB 的索引一般都使用 Suprenum Record 和 Infimum Record 来分别表示记录的上下边界 。Infimum 是比该页中任何记录都要小的值 , 而 Supremum 比该页中最大的记录值还要大 , 这两条记录在创建页的时候就有了 , 并且不会删除 。
所以 , 在 N0007 和 Suprenum Record 之间加了间隙锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
为什么不在主键上也加 GAP 锁呢?欢迎留言说出你的想法 。
二级非唯一索引 , 查询命中下图展示了 UPDATE book SET score = 9.2 WHERE Author = 'Tom' 语句在 RC 隔离等级下命中时的加锁情况 。
我们可以看到 , 在 RC 等级下 , 二级唯一索引和二级非唯一索引的加锁情况是一致的 , 都是在涉及的二级索引和对应的主键索引上加上排他记录锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
但是在 RR 隔离等级下 , 加锁的情况产生了变化 , 它不仅对涉及的二级索引和主键索引加了排他记录锁 , 还在非唯一二级索引上加了三个间隙锁 , 锁住了两个 Tom 索引值相关的三个范围 。
那为什么唯一索引不需要加间隙锁呢?间隙锁的作用是为了解决幻读 , 防止其他事务插入相同索引值的记录 , 而唯一索引和主键约束都已经保证了该索引值肯定只有一条记录 , 所以无需加间隙锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
需要注意的是 , 上图虽然画着 4 个记录锁 , 三个间隙锁 , 但是实际上间隙锁和它右侧的记录锁会合并成 Next-Key 锁 。
所以实际情况有两个 Next-Key 锁 , 一个间隙锁(Tom60,正无穷)和两个记录锁 。
二级非唯一索引 , 查询未命中下图展示了 UPDATE book SET score = 9.2 WHERE Author = 'Sarah' 在 RR 隔离等级下未命中的加锁情况 , 它会在二级索引 Rose 和 Tom 之间加间隙锁 。而 RC 隔离等级下不需要加锁 。
把MySQL中的各种锁及其原理都画出来

文章插图
 
无索引当 Where 从句的条件并不使用索引时 , 则会对全表进行扫描 , 在 RC 隔离等级下对所有的数据加排他记录锁 。在RR 隔离等级下 , 除了给记录加锁 , 还会对记录和记录之间加间隙锁 。和上边一样 , 间隙锁会和左侧的记录锁合并成 Next-Key 锁 。
下图就是 UPDATE book SET score = 9.2 WHERE score = 22 语句在两种隔离等级下的加锁情况 。


推荐阅读