MySQL Gap Lock

TLDR: 间隙锁锁定索引范围之间的间隙以防止幻读,仅在可重复读隔离级别下生效,加锁基本单位是 Next-Key Lock,会在特定条件下退化。

Content

间隙锁(Gap Lock)锁定的是索引记录之间的”间隙”,而非记录本身。它阻止其他事务在该间隙中插入新数据,是 InnoDB 防止幻读的核心机制。

触发条件

在可重复读(RR)隔离级别下,以下情况产生间隙锁:

  1. 普通索引条件查询 — MySQL 在满足条件的索引范围间隙上加锁
  2. 多列唯一索引的部分列查询 — 仅使用唯一索引的部分列时会触发
  3. 唯一索引锁定多行记录 — 范围查询时在记录间的间隙上加锁

注意:唯一索引等值查询命中已存在的记录时,不会触发间隙锁,退化为记录锁。

五条加锁规则

  1. 加锁基本单位是 Next-Key Lock(左开右闭区间)
  2. 查找过程中访问到的对象才会加锁
  3. 唯一索引范围查询会上锁到不满足条件的第一个值为止
  4. 唯一索引等值查询且记录存在时,Next-Key Lock 退化为行锁
  5. 索引等值查询未命中时,以最近的左右边界为锁定范围;非唯一索引继续向右匹配到第一个不满足条件的值,Next-Key Lock 退化为间隙锁

案例总结

以 user 表(id: 1, 5, 7, 11,age 列有普通索引)为例:

案例场景锁定范围关键规则
1唯一索引等值,记录存在 (id=5)仅锁 id=5 行规则 4:退化为行锁
2唯一索引等值,记录不存在 (id=3)(1, 5) 开区间规则 1+5:退化为间隙锁
3唯一索引范围 (id>=5 and id<6)[5, 7]规则 3:上锁到不满足条件的第一个值
4非唯一索引范围 (age>=5 and age<6)(1, 7]规则 5:非唯一索引不退化,继续向右
5间隙锁死锁(两事务获取重叠间隙锁后互相插入)(1, 5)间隙锁之间不互斥,可导致死锁
6limit 对加锁的影响 (age=6 limit 1)(5, 6]规则 2:只锁访问到的对象

关键要点

  • 间隙锁之间不互斥 — 两个事务可以同时持有同一间隙的间隙锁,这可能导致死锁
  • limit 会缩小锁定范围 — 遍历提前结束意味着访问的对象更少
  • 间隙锁区间是左开右开,临键锁区间是左开右闭
  • 从根本上避免间隙锁死锁,需要从代码层面加锁控制