很多人说“我锁的是这一行”,但 InnoDB 实际锁的是索引记录和索引间隙。Record Lock、Gap Lock、Next-Key Lock 的差别,决定了范围更新、唯一索引查询和插入等待为什么会表现得不一样。
这一篇只抓住一个核心:InnoDB 行锁建立在索引访问路径上。理解锁格式之前,先把“锁记录”和“锁范围”区分清楚,后面看死锁和间隙锁才不会混在一起。
先把机制边界说清楚
InnoDB 行锁是建立在索引上的。Record Lock 锁住已有索引记录;Gap Lock 锁住两个索引记录之间的空隙;Next-Key Lock 是记录锁加前面的间隙。不同查询条件、索引类型和隔离级别,会决定具体加哪种锁。
整体路径
上面这张图先看入口和边界:宏观上,当前读或写入语句先选择一条索引访问路径,然后沿这条路径扫描记录。扫描到的记录和范围,会根据唯一等值、普通等值、范围条件、是否命中记录等情况,转化成不同锁格式。锁的范围不是由 where 文本直接决定,而是由实际索引扫描范围决定。
第二张图再看结构关系。
底层流程
底层拆解先看数据结构。「行锁格式」至少涉及下面几类结构:
- Record Lock:锁住某个索引记录本身。
- Gap Lock:锁住索引记录之间的插入间隙。
- Next-Key Lock:锁住记录以及记录前面的间隙。
- Insert Intention Lock:插入前表达插入意图,插入意向锁之间互相兼容;真正会挡住它的通常是已有 gap lock 或 next-key lock。
- Supremum 伪记录:表示索引页上的最大边界。
再看完整执行流程:
- 执行当前读或写入语句。
- 优化器选择使用哪个索引访问。
- InnoDB 根据索引条件定位扫描范围。
- 唯一索引等值命中通常锁记录。
- 普通索引范围扫描在 RR 下可能加 next-key lock。
- 事务提交后释放相关行锁。
next-key lock 默认加锁单位是「前开后闭」区间 (a, b]——锁住记录 b 本身以及它前面的间隙。在这个默认规则之上,有几条确定性的退化规则,能把锁范围收窄:
- 唯一索引等值命中:next-key 退化为 record lock,只锁命中那一行。
- 唯一索引等值未命中:退化为 gap lock,锁住相邻间隙(不锁任何真实记录)。
- 普通索引等值:向右遍历到第一个不满足条件的值,再加 gap lock 收口。
- 范围查询:会访问到第一个不满足条件的边界值,所以锁范围往往比 where 文本看起来大一截。
记住这套退化规则,才能从一条没见过的 SQL 推出锁范围,而不是只能查表。比如 id BETWEEN 10 AND 20(主键,数据 10/20/30):先命中下界 10(record lock),再加 (10,20],最后因为范围扫描要碰到下一个不满足的边界 30,补一个 (20,30) 的 gap——合起来是 [10] + (10,20] + (20,30)。少了下界那一行,就是常见的漏判。
取舍与边界
版本差异上,MySQL 5.7 和 8.0 的 InnoDB 行锁规则主线一致。8.0 的锁观测更方便,可以直接查看 data_locks 中的 LOCK_MODE 和 LOCK_DATA。RC 隔离级别下,普通搜索和索引扫描几乎不再加 gap lock,并发性大幅提升——但要注意两个例外:外键约束检查和唯一键重复值检查(unique-check gap)在 RC 下仍然会使用 gap lock。所以「RC 绝对没有 gap lock」是个会翻车的说法,准确表述是「RC 几乎不用 gap lock,但少数内部检查场景仍会用到」。innodb_lock_wait_timeout 默认 50 秒处理的是锁等待超时,它不替代死锁检测(死锁检测由 innodb_deadlock_detect 默认开启负责,是秒级回滚)。
行锁格式最反直觉的地方,是你以为自己锁了业务行,数据库实际锁了索引范围。条件没有命中记录时也可能锁住插入间隙;查询没走预期索引时,锁范围可能比业务想象大得多。
典型问题:用机制化例子排查
范围更新的阻塞经常来自锁格式理解错误:业务以为只锁命中的行,InnoDB 却可能按索引范围加 next-key lock。先看访问路径,再看锁范围。
可以落到这些动作:
- 分析锁范围时先看执行计划使用的索引,而不是只看 where 条件。
- 高并发写入的业务唯一性要落成唯一索引,让锁更精确。
- 范围更新要控制批次大小和扫描方向,降低等待和死锁概率。
- 用 performance_schema.data_locks 观察真实锁模式,验证判断。
收束:行锁跟着索引路径走
InnoDB 行锁不是按业务直觉加的,而是沿索引访问路径落到记录和间隙上。把 Record、Gap、Next-Key 分清楚,很多阻塞和死锁才有解释。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

