往期在文章《介绍Innodb的锁机制》中提到过关于记录锁,但是没有详细展开描述。本片文章简单聊一聊。
数据库的行级锁,随着锁的细粒度不同,拥有不同的命名。
- 记录锁(Record Lock)指的是对索引记录的锁定。
- 间隙锁(Gap Lock)则是对索引记录之间的间隙进行锁定。
而Next-Key Lock则是记录锁和间隙锁的融合,同时锁定索引记录和间隙。其范围为左开右闭。
什么是Record Lock
记录锁,即Record Lock,是针对索引记录而言的锁定。例如,执行以下语句:SELECT c1 FROM t WHERE c1 = 10 FOR UPDATE; 会对满足条件c1=10的记录进行锁定,以防止其他任何事务插入、更新或删除具有相同c1值的行。
什么是Gap Lock
间隙锁,即Gap Lock,指的是针对索引记录之间的间隙,或者是在第一个索引记录之前或最后一个索引记录之后的空隙上的锁定。
在这里,所谓的“间隙”是指InnoDB索引数据结构中可供插入新值的位置。
当你使用SELECT…FOR UPDATE语句锁定一组行时,InnoDB可以创建锁,应用于索引中的实际值以及它们之间的间隙。例如,如果你选择更新所有大于10的值,间隙锁将阻止另一个事务插入新的大于10的值。
(实际会锁到+∞,这里为了演示什么是gap简化了一下)
由于锁的存在可能影响数据库的并发性,因此间隙锁只在Repeatable Reads(可重复读)这种隔离级别下才会发挥作用。
在Repeatable Reads隔离级别下,针对锁定的读操作(例如select ... for update、lock in share mode)、update操作和delete操作,会执行以下加锁操作:
- 对于具有唯一搜索条件的唯一索引,InnoDB仅锁定找到的索引记录,而不锁定间隙。
- 对于其他搜索条件,InnoDB会锁定扫描的索引范围,并使用间隙锁或next-key锁来阻止其他事务插入范围内的间隙。
换句话说,在处理**SELECT FOR UPDATE、LOCK IN SHARE MODE、UPDATE和DELETE**等语句时,除了对具有唯一搜索条件的唯一索引外,还会获取间隙锁或next-key锁,即锁定其扫描的范围。
什么是Next-Key Lock
Next-Key锁是指索引记录上的记录锁和索引记录之间间隙上的间隙锁的结合。
假设一个索引包含值10、11、13和20。此索引可能的next-key锁包括以下区间:
(-∞, 10]
(10, 11]
(11, 13]
(13, 20]
(20, ∞ ]
对于最后一个间隙,∞并不是一个真正的索引记录,因此,实际上,这个next-key锁只锁定最大索引值之后的间隙。
因此,Next-Key锁的范围都是左开右闭的。
与Gap Lock一样,Next-Key Lock只有在InnoDB的可重复读(RR)隔离级别中才会生效。
谈谈MySQL加锁机制
根据丁奇大佬《MySQL实战45讲》中的总结,加锁规则可以归纳为两个“原则”、两个“优化”和一个“bug”:
- 原则 1:加锁的基本单位是next-key lock,形成一个前开后闭的区间。
- 原则 2:只有查找过程中访问到的对象才会被加锁。
- 优化 1:对于索引上的等值查询,当给唯一索引加锁时,next-key lock会退化为行锁。
- 优化 2:对于索引上的等值查询,在向右遍历时,且最后一个值不满足等值条件时,next-key lock会退化为间隙锁。
- 一个bug:唯一索引上的范围查询会一直访问到不满足条件的第一个值为止。
当我们执行update t set d=d+1 where id = 7的时候,由于表 t 中没有 id=7 的记录,所以:
- 根据原则 1,加锁单位是 next-key lock,session A 加锁范围就是 (5,10];
- 根据优化 2,这是一个等值查询 (id=7),而 id=10 不满足查询条件,next-key lock 退化成间隙锁,因此最终加锁的范围是 (5,10)。
当我们执行select * from t where id>=10 and id10 and id=10 and c