文章目录
-
- 先说结论:三种锁的核心要点
- 行锁:精确打击,锁住一条记录
- 间隙锁:锁住"空地",防止插队
- [临键锁:行锁 + 间隙锁的合体](#临键锁:行锁 + 间隙锁的合体)
- 退化规则:什么时候锁会"缩小"
- [InnoDB 行级锁全景](#InnoDB 行级锁全景)
- 回答技巧与点评
面试被问到"MySQL 有哪些锁",你脱口而出"行锁、表锁"。面试官追问"间隙锁呢?临键锁呢?",你瞬间懵了。更头疼的是,这三个锁的关系像俄罗斯套娃,一个套一个,到底谁包含谁?
今天咱们就把 InnoDB 这三种行级锁彻底理清,让你面试时能画出来、讲明白。
先说结论:三种锁的核心要点
| 维度 | 说明 |
|---|---|
| 行锁(Record Lock) | 锁定索引中的一条记录(行) |
| 间隙锁(Gap Lock) | 锁定索引记录之间的间隙,防止插入 |
| 临键锁(Next-Key Lock) | 行锁 + 间隙锁,左开右闭区间 |
| 关系 | 临键锁 = 间隙锁 + 行锁 |
| 行锁锁定对象 | 索引记录,不是数据行 |
| 间隙锁目的 | 防止幻读,不允许往间隙里插入新记录 |
| 临键锁默认 | InnoDB 默认加锁方式 |
一句话记住:行锁锁一条,间隙锁锁一段空隙,临键锁是它俩合体------锁一条+锁一段。
行锁:精确打击,锁住一条记录
行锁是最简单直接的一种锁------锁定索引中的某一条记录。
sql
SELECT * FROM user WHERE id = 5 FOR UPDATE; -- 锁住 id=5 这一行 👈
别人可以读,但不能修改 id=5 这行,也不能用 FOR UPDATE 或 LOCK IN SHARE MODE 再锁它。
生活类比: 行锁就像图书馆里你正在看的那本书------别人可以透过玻璃橱窗看到(普通读),但不能拿走或涂改。
注意一个坑: 行锁锁的是索引,不是数据行。如果你的查询没走索引(比如用非索引字段更新),InnoDB 退化为表锁,所有行都被锁住!这就是为什么面试总强调"一定要走索引"。
间隙锁:锁住"空地",防止插队
间隙锁是 InnoDB 独有的,专门为了解决幻读问题。
假设你的表里 id 有 5、10、15 三条记录。间隙就是 (5, 10) 和 (10, 15) 以及 (-∞, 5) 和 (15, +∞) 这四段"空地"。
sql
SELECT * FROM user WHERE id > 5 AND id < 10 FOR UPDATE;
-- 间隙锁锁住 (5, 10),不允许往这个区间插入新记录 👈
别人不能往 (5, 10) 之间插入 id=6、7、8、9 的记录。 但可以修改 id=5 或 id=10 本身------因为那不是间隙,那是行。
生活类比: 间隙锁就像电影院选座------你选了 5 排和 10 排,中间的空座虽然没人坐,但不允许别人再选了,防止"插队"出现新的人。
间隙锁的特殊之处: 间隙锁之间不互斥。两个事务可以同时持有同一个间隙的间隙锁------因为间隙锁只防插入,不防读取。
临键锁:行锁 + 间隙锁的合体
临键锁(Next-Key Lock)是 InnoDB 默认的加锁方式,它是行锁和间隙锁的组合:
Next-Key Lock = Gap Lock + Record Lock
锁住的是一个左开右闭区间。比如 id 有 5、10、15,那么临键锁锁住的区间是:
- (-∞, 5]
- (5, 10]
- (10, 15]
- (15, +∞)
sql
SELECT * FROM user WHERE id = 10 FOR UPDATE;
-- 临键锁锁住 (5, 10] 区间 👈
-- 间隙部分 (5, 10):不能插入 id=6、7、8、9
-- 行锁部分 [10]:不能修改 id=10
为什么是左开右闭? 因为 InnoDB 按 B+ 树索引顺序扫描,遇到 10 时,要把前面的间隙也锁住,防止有人在 5 和 10 之间插入新记录导致幻读。
生活类比: 临键锁就像你占了一个座位,不仅占了这个座,还把前面到上一个已占座位之间的空座都占了------别人既不能坐你的座(行锁),也不能坐中间的空座(间隙锁)。
退化规则:什么时候锁会"缩小"
临键锁不是永远锁整个区间,InnoDB 有优化机制:
- 等值查询,命中记录 → 临键锁退化为行锁(只锁命中行)
- 等值查询,未命中记录 → 临键锁退化为间隙锁(只锁间隙)
- 范围查询 → 保持临键锁,不会退化
sql
-- 情况1:id=10 存在 → 退化为行锁,只锁 id=10
SELECT * FROM user WHERE id = 10 FOR UPDATE;
-- 情况2:id=7 不存在 → 退化为间隙锁,锁 (5, 10)
SELECT * FROM user WHERE id = 7 FOR UPDATE;
-- 情况3:范围查询 → 临键锁,锁 (5, 10] + (10, 15]
SELECT * FROM user WHERE id > 5 AND id <= 15 FOR UPDATE;
理解退化规则很重要,面试时能区分这三种情况,说明你真的懂了。
InnoDB 行级锁全景
InnoDB 行级锁 全景
三种锁
├── 行锁(Record Lock)── 锁定一条索引记录
├── 间隙锁(Gap Lock)── 锁定索引间隙,防插入
└── 临键锁(Next-Key Lock)── 行锁 + 间隙锁
└── 左开右闭区间 (a, b]
关系
临键锁 = 间隙锁 + 行锁
退化规则
├── 等值查询 + 命中 → 退化为行锁
├── 等值查询 + 未命中 → 退化为间隙锁
└── 范围查询 → 保持临键锁
口诀:行锁锁一条,间隙防插队,
临键是合体,退化看命中。
回答技巧与点评
标准回答
InnoDB 有三种行级锁:行锁锁定索引中的一条记录;间隙锁锁定索引记录之间的间隙,防止其他事务插入新记录;临键锁是行锁和间隙锁的组合,锁定左开右闭区间。临键锁是 InnoDB 默认的加锁方式。等值查询命中记录时临键锁退化为行锁,未命中时退化为间隙锁,范围查询保持临键锁。
加分回答
- 设计哲学:间隙锁和临键锁是 InnoDB 在可重复读(RR)隔离级别下解决幻读问题的核心机制。MVCC 解决了普通读的幻读,而临键锁解决了当前读的幻读,两者配合实现完整的 RR 隔离
- 边界情况:行锁锁的是索引不是数据行,不走索引的查询会退化为表锁;间隙锁之间不互斥,但间隙锁和插入操作互斥;在读已提交(RC)隔离级别下,间隙锁基本不生效
- 实际应用 :在线交易系统中,用
SELECT ... FOR UPDATE锁定账户行防止并发修改;用临键锁防止范围内新增记录导致的幻读。理解加锁范围对排查死锁至关重要
面试官点评
这道题考的是你对 InnoDB 锁机制的体系化理解。只会背三种锁的名字只能拿基础分。能说清临键锁的组成关系、退化规则、以及和幻读的关系,才能拿高分。如果你能提到不同隔离级别下锁的行为差异,面试官会认为你有实战深度。
内容有帮助?点赞、收藏、关注三连!评论区等你 💪