MySQL 锁问题最麻烦的地方,是表面现象都像“SQL 卡住了”。但卡住的原因可能是 MDL、表锁、行锁、间隙锁、外键检查,也可能是死锁检测后的回滚。
锁不是为了让系统变慢,而是在并发修改同一批结构或数据时维持事务语义。排查锁问题,不能只看哪条 SQL 慢,还要看它沿哪个索引访问、持有哪些锁、等待的是记录还是范围。
先把机制边界说清楚
MySQL 的锁可以粗略分为 Server 层的元数据锁、存储引擎层的表级意向锁和 InnoDB 行锁体系。锁的目标不是让系统变慢,而是在并发修改同一批数据时,保证结构、记录和事务语义不被破坏。
整体路径
上面这张图先看入口和边界:宏观上,一条写 SQL 进入 MySQL 后,Server 层先拿 MDL 保证表结构稳定;InnoDB 再根据访问路径拿意向锁和行锁;如果范围条件涉及可插入空隙,还可能出现 gap 或 next-key lock。锁持有到事务结束,等待关系会形成排队或死锁。
第二张图再看结构关系。
底层流程
底层拆解先看数据结构。「锁」至少涉及下面几类结构:
- MDL:保护表结构元数据,DDL 和 DML 都会参与。
- 意向锁:表级标记,说明事务准备在表内加行锁。
- 记录锁:锁住具体索引记录。
- 间隙锁与 next-key lock:控制范围内插入,减少幻读。
- 等待图:事务之间持有和等待锁形成的关系。
再看完整执行流程:
- SQL 进入 Server 层并申请 MDL。
- 优化器选择访问路径,决定会扫描哪些索引范围。
- InnoDB 按记录或范围加行锁。
- 锁冲突时事务进入等待。
- 检测到等待环时触发死锁回滚,否则等待超时或对方提交。
- 事务提交或回滚后释放持有锁。
取舍与边界
版本差异上,MySQL 8.0 通过 performance_schema.data_locks 和 data_lock_waits 提供更直接的锁观测能力。5.7 时代更多依赖 information_schema 和 SHOW ENGINE INNODB STATUS。锁语义本身仍然与索引访问路径紧密绑定。
锁的短板是它经常被错误 SQL 放大。没有合适索引的 update 会扫描并锁住大量记录;长事务让锁迟迟不释放;DDL 等 MDL 可能被一个未提交事务挡住,进而阻塞后续所有访问。
典型问题:用机制化例子排查
锁等待排查不要只问“哪条 SQL 卡住了”,而要还原等待关系:当前事务持有哪些锁,正在等哪些锁,索引访问范围有没有被放大,长事务有没有拖住释放。
可以落到这些动作:
- 锁排障先分清 MDL、行锁等待和死锁,不要一律改索引。
- 所有写 SQL 都要确认访问路径,避免无索引范围更新。
- 事务里不要夹远程调用、用户交互和大批量循环。
- 上线 DDL 前检查长事务,并准备回滚和限流方案。
收束:锁问题先还原等待关系
锁不是慢的同义词,而是并发控制的边界。排查锁问题时,先还原谁持有、谁等待、锁住的是记录还是范围,方案自然会收敛很多。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

