Next-Key Lock的加锁逻辑

image.png

  1. 加锁时以Next-Key(下一条行记录)为基本单位
  2. 查找过程中扫描过的范围才加锁
  3. 唯一索引等值查询,没有间隙锁,只加行锁
  4. 索引等值查询最后一个扫描到的不满足条件值不加行锁
  5. 索引覆盖且只加S锁时,不锁主键索引

image.png

等值查询间隙锁

image.png
select * from t3 where id = 11 for update 这一行的意思是选出id=11的行数据,加X锁(写锁/独占锁)
由于ID= 11 落在(10,20)之间,所以(10,20)加了间隙锁,但由于我们是基于Next-key为单位的,也就是间隙和它的下一行记录。所以加到了(10,20],也就是一开始把20给锁上了,但还有一个条件是,等值查询最后扫描到的一个键如果不满足规则,要把行锁撤销掉,比对11和20不一样,就把20的行锁撤销掉了,最后这个语句锁上的只有(10,20)这个间隙

我们是基于Next-key为单位的,所以
等值查询最后一个扫描不满足11,就把(next)行锁撤销掉,索引
Tx B无法插入,避免了不可重复读

非唯一索引等值锁

image.png
select id from t3 where c = 10 lock in share mode,加了s锁,走了索引覆盖,
因为它不是唯一索引,所以不知道10右边还有没有10,所以继续往右边扫描,扫描近了(10,20)这个间隙,一旦扫描到了间隙,又要上一次next-key lock。把(10,20]锁上,再继续扫描,扫描20,判断20跟10不相等,所以它断定后面没有跟自己相等的索引了,20就是最后一个扫描到的索引,但是20不等于10,所以就把20的行锁给取消了。
所以Tx B的第一句是可以加锁成功的。

主键索引范围锁

image.png
为什么首先等值查询20,只锁行锁,而(10,20)不能被锁?
因为主键索引:

  1. 唯一索引等值查询,没有间隙锁,只加行锁

只有等值存在才加行锁,等值不存在才加间隙锁
然后往右扫描,大于20的有多少,锁上了(20,30)这个间隙锁和30这个行锁。
如果是非唯一的索引,那么还会往右扫描,锁上(30,40)这个间隙锁,
因为是主键索引,索引后面肯定没有等于30的了,扫描到30就停止了。
所以锁到了[20,30]
所以TX b不能锁上20,但是可以插入新纪录。

非唯一索引范围锁

image.png
后面还可能出现等于30的,所以还会扫描(30,40),由于40不等于30,就撤销了40的行锁。

非索引字段查询

image.png

总结

目的:在可重复读的隔离级别下部分预防幻读
手段:Next-Key Lock(行锁+间隙锁)
规则复杂,个人认为并不优雅
注意:当前读时,不要查询没有索引的字段