看到 Deadlock found when trying to get lock,不代表数据库坏了。死锁是并发系统里的等待环:事务 A 等 B 手里的资源,事务 B 又等 A 手里的资源,系统只能回滚其中一个让链路继续往前走。
排查死锁的重点不是责怪某条 SQL,而是还原等待图:谁先拿了什么锁,后来又等什么锁,索引访问范围有没有被放大,事务顺序能不能调整。
先把机制边界说清楚
死锁的本质是等待图中出现环。事务 A 持有资源 1 等资源 2,事务 B 持有资源 2 等资源 1,就形成闭环。InnoDB 会检测等待关系,选择代价较小的事务作为 victim 回滚。
整体路径
上面这张图先看入口和边界:宏观上,死锁排查要从错误日志和 SHOW ENGINE INNODB STATUS 进入,找到最近一次死锁报告,抽取两个事务的 SQL、持有锁、等待锁和索引信息,再还原它们为什么以不同顺序访问同一批资源。
第二张图再看结构关系。
底层流程
底层拆解先看数据结构。「死锁排查」至少涉及下面几类结构:
- 事务:死锁图中的节点,包含当前 SQL、已持有锁和等待锁。
- 锁资源:索引记录、间隙、next-key 范围或 MDL。
- 等待边:事务等待另一个事务释放资源。
- 死锁检测器:周期性或等待时检查等待图是否成环。
- victim 回滚:打破环路的代价选择。
再看完整执行流程:
- 多个事务并发获取锁。
- 事务之间出现互相等待。
- InnoDB 构建或检查等待关系。
- 发现等待图成环后选择一个事务回滚。
- 被回滚事务返回 deadlock 错误,其他事务继续执行。
- 应用侧按幂等语义重试或上报。
取舍与边界
版本差异上,MySQL 8.0 可以结合 performance_schema.data_locks、data_lock_waits 和 InnoDB status 更完整地还原死锁现场。高热点场景下,innodb_deadlock_detect 本身也可能带来 CPU 压力,极端情况下会考虑关闭检测并依赖锁等待超时,但这需要非常谨慎。
死锁的短板是它经常由业务访问顺序不一致触发,而不是某条 SQL 单独有错。只盯被回滚那条 SQL,容易忽略另一个事务持有什么锁。没有幂等重试的应用,把一次可恢复死锁变成了用户可见失败。
典型问题:用机制化例子排查
死锁排查可以从等待图入手,而不是从业务流程猜。把每个事务已持有的锁和正在等待的锁列出来,环出现在哪里,问题就基本清楚了。
可以落到这些动作:
- 排查死锁先画等待图,不要只看最后报错 SQL。
- 让同类业务按固定顺序访问资源,减少环路形成概率。
- 缩短事务并减少范围锁,避免把无关记录拉进等待图。
- 应用必须识别死锁错误并做有边界的幂等重试。
收束:死锁是等待图的问题
死锁不是玄学,也不一定是谁写错了 SQL。只要把事务的持锁和等待关系画出来,环在哪里,治理方向就在哪里。
关于十三Tech
我是十三,All in AI Agent 方向的架构师,专注 AI 工程实践。
我相信 AI 是程序员的最佳搭档,也希望帮助每一位开发者更好地驾驭 AI。
如果你想继续跟完这套「图解 MySQL」,欢迎关注公众号 「十三Tech」。后续会继续按机制、图解和实战排查这条线更新。

