极高 困难
间隙锁与幻读#
一句话答案#
间隙锁锁住索引记录之间的间隙防止插入,配合记录锁组成临键锁(Next-Key Lock),在 RR 级别解决幻读。
核心要点
幻读: 同一事务两次查询返回不同的行数(新插入的行)
RR级别解决幻读: 快照读靠MVCC / 当前读靠间隙锁+临键锁
临键锁(Next-Key Lock): 记录锁 + 间隙锁 = 左开右闭区间
加锁推演:Next-Key Lock 会按情况退化/延伸(面试最爱追问):
InnoDB 加锁不是无脑加 Next-Key Lock,而是动态推演。掌握下面四条就能回答”这条 SQL 锁了哪些区间”(前提 RR + 当前读):
| 场景 | 加锁结果 | 原因 |
|---|---|---|
| 唯一索引等值命中 | 退化为记录锁,不加间隙 | 唯一值不可能再插入重复行,无幻读风险 |
| 唯一索引等值未命中 | 退化为间隙锁,锁该值所在间隙 | 没有记录可锁,只需挡住插入 |
| 二级索引等值命中 | 索引上 Next-Key Lock + 向右延伸锁到第一个不满足值的间隙 + 回表对主键加记录锁 | 二级索引可重复,必须锁间隙防插入;回表锁主键防绕过 |
| 范围查询 | Next-Key Lock 从首个满足区间一路向右锁到第一个不满足条件的记录所在区间 | 覆盖整个范围边界防插入 |
示例(主键 id=1,5,10,15):
WHERE id=10(命中)→ 仅记录锁 id=10WHERE id=7(未命中)→ 间隙锁 (5,10)WHERE id>=10→ (5,10] + (10,15] + (15,+∞)
详细推演与二级索引回表加锁见 MySQL锁机制。
面试回答(2分钟版)
幻读是同一事务内两次按相同条件查询,第二次出现了第一次没有的新行,本质是其他事务在查询范围内插入了数据。InnoDB 在 RR 级别通过两种机制解决幻读:快照读靠 MVCC 的 Read View 保证数据快照一致;当前读如 SELECT FOR UPDATE、UPDATE、DELETE 靠 Next-Key Lock。Next-Key Lock 是记录锁和间隙锁的组合,锁定前开后闭区间,比如索引值有 1、5、10,对 id=5 做当前读会加 (1, 5] 的 Next-Key Lock,记录锁锁住 id=5 防止修改删除,间隙锁锁住 (1, 5) 防止插入新行。间隙锁只在 RR 级别存在,RC 没有间隙锁因此无法防幻读。需要注意间隙锁之间不互斥,但间隙锁和插入意向锁互斥,这是间隙锁容易引发死锁的原因。业务上不需要防幻读且并发要求高时可降到 RC 避免间隙锁带来的锁等待。
追问与易错
追问方向:
- “间隙锁只在 RR 级别有吗?”→ 是的,间隙锁只在 REPEATABLE READ 级别下存在;READ COMMITTED 没有间隙锁,因此无法防止幻读但并发性能更好
- “间隙锁会导致死锁吗?举例?”→ 会,典型场景:事务 A 锁住间隙 (5, 10) 准备插入 id=7,事务 B 锁住间隙 (5, 10) 准备插入 id=8,两者的插入意向锁互相等待对方释放间隙锁形成死锁
- “如何避免间隙锁导致的性能问题?”→ 尽量用精确等值查询减少锁范围;业务允许时降低隔离级别到 RC 消除间隙锁;避免大范围的 UPDATE/DELETE 操作锁住过多间隙
易错点:
- ❌ “间隙锁锁的是行”——间隙锁锁的是索引记录之间的「间隙」,不锁已有记录
- ❌ “RC 级别不会有锁冲突”——RC 仍有记录锁,只是没有间隙锁