MySQL 是怎么加行级锁的?为什么一会是 next-key 锁,一会是间隙锁,一会又是记录锁?( 七 )

  • 当查询的记录「不存在」时 , 扫描到第一条不符合条件的二级索引记录,该二级索引的 next-key 锁会退化成间隙锁 。因为不存在满足查询条件的记录,所以不会对主键索引加锁 。
  • 接下里用两个实验来说明 。
    1、记录不存在的情况
    实验一:针对非唯一索引等值查询时,查询的值不存在的情况 。
    先来说说非唯一索引等值查询时 , 查询的记录不存在的情况,因为这个比较简单 。
    假设事务 A 对非唯一索引(age)进行了等值查询,且表中不存在 age = 25 的记录 。
    mysql> begin;Query OK, 0 rows affected (0.00 sec)mysql> select * from user where age = 25 for update;Empty set (0.00 sec)事务 A 加锁变化过程如下:
    • 定位到第一条不符合查询条件的二级索引记录 , 即扫描到 age = 39,于是该二级索引的 next-key 锁会退化成间隙锁,范围是 (22, 39) 。
    • 停止查询
    事务 A 在 age = 39 记录的二级索引上 , 加了 X 型的间隙锁,范围是 (22, 39) 。意味着其他事务无法插入 age 值为 23、24、25、26、....、38 这些新记录 。不过对于插入 age = 22 和 age = 39 记录的语句,在一些情况是可以成功插入的,而一些情况则无法成功插入,具体哪些情况,会在后面说 。
    MySQL 是怎么加行级锁的?为什么一会是 next-key 锁,一会是间隙锁,一会又是记录锁?

    文章插图
    我们也可以通过 select * from performance_schema.data_locks\G; 这条语句来看看事务 A 加了什么锁 。
    输出结果如下,我这里只截取了行级锁的内容 。
    MySQL 是怎么加行级锁的?为什么一会是 next-key 锁,一会是间隙锁,一会又是记录锁?

    文章插图
    从上图的分析,可以看到,事务 A 在 age = 39 记录的二级索引上(INDEX_NAME: index_age ),加了范围为 (22, 39) 的 X 型间隙锁 。
    此时,如果有其他事务插入了 age 值为 23、24、25、26、....、38 这些新记录,那么这些插入语句都会发生阻塞 。不过对于插入 age = 39 记录的语句,在一些情况是可以成功插入的,而一些情况则无法成功插入 , 具体哪些情况,接下来我们就说!
    当有一个事务持有二级索引的间隙锁 (22, 39) 时,什么情况下,可以让其他事务的插入 age = 22 或者 age = 39 记录的语句成功?又是什么情况下 , 插入 age = 22 或者 age = 39 记录时的语句会被阻塞?
    我们先要清楚,什么情况下插入语句会发生阻塞 。
    插入语句在插入一条记录之前 , 需要先定位到该记录在 B+树 的位置,如果插入的位置的下一条记录的索引上有间隙锁,才会发生阻塞 。
    在分析二级索引的间隙锁是否可以成功插入记录时,我们要先要知道二级索引树是如何存放记录的?
    二级索引树是按照二级索引值(age列)按顺序存放的,在相同的二级索引值情况下, 再按主键 id 的顺序存放 。知道了这个前提 , 我们才能知道执行插入语句的时候,插入的位置的下一条记录是谁 。
    基于前面的实验,事务 A 是在 age = 39 记录的二级索引上,加了 X 型的间隙锁,范围是 (22, 39) 。
    插入 age = 22 记录的成功和失败的情况分别如下:
    • 当其他事务插入一条 age = 22,id = 3 的记录的时候,在二级索引树上定位到插入的位置,而该位置的下一条是 id = 10、age = 22 的记录,该记录的二级索引上没有间隙锁,所以这条插入语句可以执行成功 。
    • 当其他事务插入一条 age = 22 , id = 12 的记录的时候 , 在二级索引树上定位到插入的位置,而该位置的下一条是 id = 20、age = 39 的记录,正好该记录的二级索引上有间隙锁,所以这条插入语句会被阻塞,无法插入成功 。
    插入 age = 39 记录的成功和失败的情况分别如下:
    • 当其他事务插入一条 age = 39 , id = 3 的记录的时候,在二级索引树上定位到插入的位置,而该位置的下一条是 id = 20、age = 39 的记录,正好该记录的二级索引上有间隙锁,所以这条插入语句会被阻塞,无法插入成功 。
    • 当其他事务插入一条 age = 39,id = 21 的记录的时候,在二级索引树上定位到插入的位置,而该位置的下一条记录不存在,也就没有间隙锁了,所以这条插入语句可以插入成功 。
    所以,当有一个事务持有二级索引的间隙锁 (22, 39) 时,插入 age = 22 或者 age = 39 记录的语句是否可以执行成功,关键还要考虑插入记录的主键值,因为「二级索引值(age列)+主键值(id列)」才可以确定插入的位置 , 确定了插入位置后 , 就要看插入的位置的下一条记录是否有间隙锁,如果有间隙锁 , 就会发生阻塞,如果没有间隙锁,则可以插入成功 。

    推荐阅读