以下的查询全部都是 当前读 select … for update
所用到的表:
唯一索引等值查询
通过唯一索引等值查询出来结果时,next-key lock 会退化成 记录锁(只锁一行)
通过唯一索引等值没有查询出来结果时,next-key lock 会退化成 间隙锁
唯一索引范围查询
(会退化为间隙锁 或者 记录锁)
比如 id大于等于5,那么id=5的条件还是按照唯一索引等值查询 的条件进行退化,而大于5的部分则变为间隙锁。
-
最开始要找的第一行是 id = 8,因此 next-key lock(4,8],但是由于 id 是唯一索引,且该记录是存在的,因此会退化成记录锁,也就是只会对 id = 8 这一行加锁;
-
由于是范围查找,就会继续往后找存在的记录,也就是会找到 id = 16 这一行停下来,然后加 next-key lock (8, 16],但由于 id = 16 不满足 id < 9,所以会退化成间隙锁,加锁范围变为 (8, 16)。
非唯一索引等值查询
- 用非唯一索引等值查询时,如果记录存在,那么除了会加 next-key lock (间隙锁+记录锁)外,还额外加间隙锁,也就是会加两把锁(相当于两个间隙锁和一个记录锁,间隙锁一个向上加,一个向下加,记录锁 锁住查到的那行记录)
- 当记录不存在时,只会加 next-key lock,然后退化成间隙锁,也就是只加一把锁
非唯一索引等值查询 查到了数据的情况:
会话 1 加锁变化过程如下:
- 先会对普通索引 b 加上 next-key lock,范围是(4,8];
- 然后因为是非唯一索引,且查询的记录是存在的,所以还会加上间隙锁,规则是向下遍历到第一个不符合条件的值才能停止,因此间隙锁的范围是(8,16)。
所以,会话1的普通索引 b 上共有两个锁,分别是 next-key lock (4,8] 和间隙锁 (8,16) 。
那么,当会话 2 往间隙锁里插入 id = 9 的记录就会被锁住,而会话 3 和会话 4 是因为更改了 next-key lock 范围里的记录而被锁住的。
注意这里:我们不能修改b=8的记录是因为这一行加了记录锁,而不是因为间隙锁,加间隙锁还是可以修改记录,但加记录锁就不能修改记录了。
然后因为 b = 16 这条记录没有加锁,所以会话 5 是可以正常执行的。
非唯一索引等值查询 没有查到数据的情况:
会话 1 加锁变化过程如下:
- 先会对普通索引 b 加上 next-key lock,范围是(8,16];
- 但是由于查询的记录是不存在的,所以不会再额外加个间隙锁,但是 next-key lock 会退化为间隙锁,最终加锁范围是 (8,16)。
会话 2 因为往间隙锁里插入了 b = 9 的记录,所以会被锁住,而 b = 16 是没有被加锁的,因此会话 3 的语句可以正常执行
非唯一索引范围查询
(一定不会退化 为间隙锁 或者 记录锁)和唯一索引范围查询 注意区分
-
最开始要找的第一行是 b = 8,因此 next-key lock(4,8]
-
由于是范围查找,就会继续往后找存在的记录,也就是会找到 b = 16 这一行停下来,然后加 next-key lock (8, 16]
所以,会话 1 的普通索引 b 有两个 next-key lock,分别是 (4,8] 和(8, 16]。这样,你就明白为什么会话 2 、会话 3 、会话 4 的语句都会被锁住了
总结
-
唯一索引等值查询:
- next-key lock 会退化为记录锁 或 间隙锁
-
非唯一索引等值查询:
- 记录存在,会加next-key lock 和 间隙锁
- 记录不存在,会加间隙锁
-
非唯一索引和唯一索引的范围查询的加锁规则不同之处在于:
- 唯一索引 在满足一些条件时,next-key lock 会退化为记录锁 或 间隙锁
- 非唯一索引,如何时候都不会退化