show engine innodb status 中的死锁信息位于 latest detected deadlock 小节,是内存快照、瞬时存在且每次执行即覆盖;需在报错“deadlock found”后立即执行并加\g查看,重点分析各事务的持有锁、等待锁及最后sql,结合innodb_trx等表定位长事务与锁等待关系。

怎么看 SHOW ENGINE INNODB STATUS 里的死锁段落
MySQL 的死锁信息只在发生后短暂保留在 SHOW ENGINE INNODB STATUS 输出中,且每次执行会覆盖前一次结果,所以必须在死锁刚发生时立刻抓取。输出里真正有用的死锁部分位于 LATEST DETECTED DEADLOCK 小节,它不是日志文件,而是内存快照——这意味着如果没及时执行命令,这段信息就丢了。
实操建议:
- 遇到业务报错
Deadlock found when trying to get lock后,**立刻连上对应 MySQL 实例执行SHOW ENGINE INNODB STATUS\G**(注意加\G让输出可读) - 不要依赖慢查询日志或 general log,它们不记录死锁细节
- 如果应用是集群部署,要确认连接的是**实际报错的那个实例**,而非负载均衡后的任意节点
死锁日志里关键字段怎么看:transaction、holding lock、waiting for lock
一段典型的死锁日志会列出至少两个事务(TRANSACTION),每个事务包含三类核心信息:它自己持有的锁(HELD LOCKS)、正在等待的锁(WAITING FOR THIS LOCK TO BE GRANTED)、以及最后执行的 SQL(mysql tables in use 或 Trx has been waiting 上方的语句)。
判断死锁成因,重点比对两者的锁冲突链:
- 事务 A 持有
PRIMARY上的 record lock,却在等idx_name上的 next-key lock;事务 B 反过来持有idx_name锁,等待PRIMARY锁 → 典型的交叉持锁 - 注意
lock_mode X locks rec but not gap表示行锁(非间隙),而X locks gap before rec是间隙锁,后者容易被忽略但常引发死锁 - 如果看到
lock_mode X insert intention,说明有插入意向锁冲突,往往出现在INSERT ... SELECT或唯一键冲突重试场景
为什么 SHOW ENGINE INNODB STATUS 看不到完整 SQL?
MySQL 默认只保留事务最后执行的那条 SQL(可能被截断),不会回溯整个事务过程。尤其当事务包含多条 DML 时,你看到的“最后 SQL”未必是加锁源头——比如先 UPDATE 了某行,再 SELECT ... FOR UPDATE 触发冲突,但日志里只显示后者。
补救办法:
- 开启
innodb_print_all_deadlocks = ON(5.6.2+),让死锁信息写入 error log,虽然仍不记录全事务,但能持久化、不丢失 - 配合
general_log = ON+ 时间戳定位(仅限低流量环境,性能损耗大) - 在应用层对关键事务加唯一 trace_id,并打点记录每条 SQL 执行顺序,和死锁时间对齐
用 information_schema.INNODB_TRX 和 INNODB_LOCK_WAITS 辅助验证
SHOW ENGINE INNODB STATUS 是快照,而 INNODB_TRX 等表是实时视图,二者结合才能确认当前是否仍有未提交事务在“埋雷”。比如死锁已解,但某个事务迟迟不提交,它持有的锁仍可能阻塞后续操作。
常用查法:
- 查活跃长事务:
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW()) - TIME_TO_SEC(TRX_STARTED) > 60 - 查锁等待关系:
SELECT * FROM information_schema.INNODB_LOCK_WAITS,关联INNODB_TRX能看到谁在等谁 - 注意
TRX_STATE = 'LOCK WAIT'的事务,正是死锁检测器挑出来准备 kill 的那个(通常会被干掉,但偶尔因并发高残留)
死锁本身是瞬态现象,但背后暴露的事务设计缺陷(比如更新顺序不一致、事务粒度太大、缺失索引导致锁升级)才是真问题。别只盯着那一段日志,得顺藤摸到应用代码里 SQL 的执行路径。











