Next-Key Lock的加锁逻辑

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

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无法插入,避免了不可重复读
非唯一索引等值锁

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的第一句是可以加锁成功的。
主键索引范围锁

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

后面还可能出现等于30的,所以还会扫描(30,40),由于40不等于30,就撤销了40的行锁。
非索引字段查询
总结
目的:在可重复读的隔离级别下部分预防幻读
手段:Next-Key Lock(行锁+间隙锁)
规则复杂,个人认为并不优雅
注意:当前读时,不要查询没有索引的字段
