很多人说“我锁的是这一行”,但 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:插入前表达插入意图,多个不同位置插入可以兼容。
- Supremum 伪记录:表示索引页上的最大边界。
再看完整执行流程:
- 执行当前读或写入语句。
- 优化器选择使用哪个索引访问。
- InnoDB 根据索引条件定位扫描范围。
- 唯一索引等值命中通常锁记录。
- 普通索引范围扫描在 RR 下可能加 next-key lock。
- 事务提交后释放相关行锁。
取舍与边界
版本差异上,MySQL 5.7 和 8.0 的 InnoDB 行锁规则主线一致。8.0 的锁观测更方便,可以直接查看 data_locks 中的 LOCK_MODE 和 LOCK_DATA。RC 隔离级别下 gap lock 使用更少,但并不代表所有间隙锁都消失。
行锁格式最反直觉的地方,是你以为自己锁了业务行,数据库实际锁了索引范围。条件没有命中记录时也可能锁住插入间隙;查询没走预期索引时,锁范围可能比业务想象大得多。
典型问题:用机制化例子排查
范围更新的阻塞经常来自锁格式理解错误:业务以为只锁命中的行,InnoDB 却可能按索引范围加 next-key lock。先看访问路径,再看锁范围。
可以落到这些动作:
- 分析锁范围时先看执行计划使用的索引,而不是只看 where 条件。
- 高并发写入的业务唯一性要落成唯一索引,让锁更精确。
- 范围更新要控制批次大小和扫描方向,降低等待和死锁概率。
- 用 performance_schema.data_locks 观察真实锁模式,验证判断。
收束:行锁跟着索引路径走
InnoDB 行锁不是按业务直觉加的,而是沿索引访问路径落到记录和间隙上。把 Record、Gap、Next-Key 分清楚,很多阻塞和死锁才有解释。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

