参考:高性能 MySQL(第 4 版) · MySQL 技术内幕:InnoDB 存储引擎(第 2 版) · MySQL 8.4 Reference Manual。每章含可暂停 Lab 动画 + 面试问答 + 书籍章节对照。
MySQL 面试 · Lab C · 锁与并发控制
锁与并发控制
主问题
-- 表里有 10、20、30;A 先执行
SELECT * FROM t WHERE id BETWEEN 10 AND 20 FOR UPDATE;为什么另一事务 INSERT id=15 会被阻塞?
这题真正考什么
- · 不是考你背 record / gap / next-key 这些名词。
- · 而是考你能不能把范围锁映射到索引轴上的区间和间隙。
学完你要能做到
- · 画出 record / gap / next-key 在索引轴上的区间
- · 解释 insert intention 为什么等待 gap lock
- · 说明唯一索引等值为什么通常不锁 gap
- · 把访问路径和锁范围联系起来
A 的范围锁定读 BETWEEN 10 AND 20 在 RR 下持有(10,20] next-key lock,里面含 (10,20) 这段 gap。
INSERT 15 落在被锁间隙里,insert intention 必须等 A 释放锁,所以被阻塞。
换成唯一索引等值命中,能精确定位一条记录,就通常不锁 gap。
学习路线
点节点跳转 · 滚动时自动高亮当前位置
这道题到底在问什么?
表面问「INSERT 15 为什么被阻塞」,实际是在问:你能不能把范围锁映射到索引轴上的区间和间隙。
A 锁住 [10,20] 区间(含 (10,20) 间隙)→ id=15 想插进琥珀色间隙,被挡住。
行锁到底锁什么?
InnoDB 的「行锁」其实锁在索引上,理解这点后面才不会卡。
锁的载体
锁在索引记录上
行锁锁的是索引记录,不是抽象的「物理行」。走哪个索引,就锁那个索引上的项。
有索引
锁精确、范围小
命中具体索引记录时,只锁到需要的那几条 / 那段区间。
无可用索引
退化为大范围锁
没有合适索引时只能扫聚簇索引,可能锁住大量记录和间隙,并发骤降。
record / gap / next-key 三种锁
三个名字看着像,但锁的对象和目的完全不同。
record lock
锁已有索引记录
锁住一条已存在的索引记录,阻止它被修改或删除。命中谁就锁谁。
gap lock
锁记录间的间隙
锁住记录之间的间隙,本身不锁记录,作用是阻止往这段间隙插入新记录。
next-key lock
record + 前间隙
= record lock + 前面的 gap lock,是 RR 范围扫描防幻读的主力。
锁是怎么加上去的?
切换访问路径,逐步看 record / gap / next-key 落在索引轴的哪一段。
索引轴:record / gap / next-key 在 (-∞,+∞) 的具体位置
场景:普通索引范围查询 FOR UPDATE · 表中已有索引记录 10、20、30
锁住已经存在的索引记录(如 10、20)
锁住两个索引值之间的空隙(如 (10,20))
一个 record + 它前面的 gap,覆盖一段半开区间
当前发生了什么
A 执行 SELECT ... WHERE id BETWEEN 10 AND 20 FOR UPDATE,这是当前读,沿主键索引扫描 [10,20] 区间。
为什么会这样
锁加在索引上,必须先确定扫描哪些记录,才能确定锁范围。
面试怎么回答
先说访问路径:唯一索引等值、普通索引范围、还是无索引扫描,决定锁范围。
常见误区
别先背锁名,锁范围是由执行计划 / 扫描路径决定的。
访问路径决定锁范围
同一张表,访问路径不同,锁范围天差地别。
| 访问路径 | 典型锁 | 会不会锁 gap |
|---|---|---|
| 唯一索引等值命中 | 只锁那一条 record | 通常不锁 gap |
| 普通索引范围 | record + next-key | 锁 gap,防区间插入 |
| 无可用索引 | 扫聚簇索引,锁大量记录/间隙 | 锁很多 gap,危险 |
易混概念对比
五种锁一次对齐,面试里别再说串。
| 锁 | 锁的对象 | RR 范围扫描会用吗 |
|---|---|---|
| record lock | 已有索引记录 | 会 |
| gap lock | 记录之间的间隙 | 会 |
| next-key lock | 记录 + 前面的间隙 | 会(默认) |
| insert intention | 插入位置的意向 | 插入时 |
| 意向锁 IS/IX | 表级标记 | 不直接锁行 |
面试回答生成器
按时间预算选一档念出来。
A 的范围锁定读 BETWEEN 10 AND 20 在 RR 下持有 (10,20] 的 next-key lock,里面包含 (10,20) 这段 gap。
INSERT 15 落在这段被锁的间隙里,insert intention 必须等 A 释放锁,所以被阻塞。
追问链 + 复习清单
勾掉每一条,这道题就算通关。
通关检查
0/5 已掌握追问链
- QSELECT 普通快照读会不会加 next-key lock?
- Q死锁日志里应该重点看哪些信息?
- Q如何通过索引设计缩小锁范围?