MySQL Gap Lock
TLDR: 间隙锁锁定索引范围之间的间隙以防止幻读,仅在可重复读隔离级别下生效,加锁基本单位是 Next-Key Lock,会在特定条件下退化。
Content
间隙锁(Gap Lock)锁定的是索引记录之间的”间隙”,而非记录本身。它阻止其他事务在该间隙中插入新数据,是 InnoDB 防止幻读的核心机制。
触发条件
在可重复读(RR)隔离级别下,以下情况产生间隙锁:
- 普通索引条件查询 — MySQL 在满足条件的索引范围间隙上加锁
- 多列唯一索引的部分列查询 — 仅使用唯一索引的部分列时会触发
- 唯一索引锁定多行记录 — 范围查询时在记录间的间隙上加锁
注意:唯一索引等值查询命中已存在的记录时,不会触发间隙锁,退化为记录锁。
五条加锁规则
- 加锁基本单位是 Next-Key Lock(左开右闭区间)
- 查找过程中访问到的对象才会加锁
- 唯一索引范围查询会上锁到不满足条件的第一个值为止
- 唯一索引等值查询且记录存在时,Next-Key Lock 退化为行锁
- 索引等值查询未命中时,以最近的左右边界为锁定范围;非唯一索引继续向右匹配到第一个不满足条件的值,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) | 间隙锁之间不互斥,可导致死锁 |
| 6 | limit 对加锁的影响 (age=6 limit 1) | (5, 6] | 规则 2:只锁访问到的对象 |
关键要点
- 间隙锁之间不互斥 — 两个事务可以同时持有同一间隙的间隙锁,这可能导致死锁
- limit 会缩小锁定范围 — 遍历提前结束意味着访问的对象更少
- 间隙锁区间是左开右开,临键锁区间是左开右闭
- 从根本上避免间隙锁死锁,需要从代码层面加锁控制
Related Pages
- MySQL InnoDB Locks — 概览
- MySQL Record Lock — 记录锁
- MySQL Next-Key Lock — 临键锁