<p>死锁详情在SHOW ENGINE INNODB STATUS输出中位于LATEST DETECTED DEADLOCK区块下,含 (1)和 (2)两个事务块,需对照HOLDS THE LOCK(S)与WAITING FOR THIS LOCK TO BE GRANTED分析闭环,并结合索引、SQL及隔离级别定位根因。</p>

SHOW ENGINE INNODB STATUS 输出里哪段才是死锁详情
它不在开头,也不在结尾,而是固定出现在 TRANSACTIONS 小节末尾、以 *** (1) WAITING FOR THIS LOCK TO BE GRANTED: 开头的那块内容。MySQL 每次检测到死锁后,会把最近一次的完整死锁信息“快照”写进这里,覆盖前一次——也就是说,SHOW ENGINE INNODB STATUS 只保留最后一次,不是历史日志。
常见误区是盯着 LATEST DETECTED DEADLOCK 这行标题猛看,其实它只是个分隔标记,真正有用的细节全在它下面的两段事务描述里:一段标着 *** (1)(被回滚的事务),一段标着 *** (2)(成功执行的事务)。
怎么快速定位并提取关键字段
重点盯住四类信息,每段事务里都有:
-
TRANSACTION后的十六进制ID(如4294967296),用于关联information_schema.INNODB_TRX查原始SQL -
mysql tables in use和locked tables—— 看涉及哪些表,是否跨表加锁 -
LOCK WAIT行里的index名称,确认锁的是哪个索引,不是表名也不是列名 -
lock_mode X locks rec but not gap waiting这类描述,区分是记录锁、间隙锁还是临键锁;waiting表示正在等锁,holding lock(s)表示已持有锁
如果看到 lock_mode X locks gap before rec,基本可以断定是间隙锁引发的死锁,和 WHERE 条件没走索引或范围查询强相关。
为什么刚执行完死锁报错却看不到详情
因为 SHOW ENGINE INNODB STATUS 的输出是异步刷新的,不是实时镜像。从死锁发生 → MySQL 回滚事务 → 更新状态缓冲区 → 你执行命令,中间有毫秒级延迟。如果在这期间有其他事务触发了新的死锁,就会覆盖掉你想看的那一次。
实操建议:
- 线上出问题时,别只跑一次
SHOW ENGINE INNODB STATUS,立刻连跑 3–5 次,取最早那次结果(最可能保留原貌) - 配合查
SELECT * FROM information_schema.INNODB_TRX ORDER BY trx_started DESC LIMIT 5;,看有没有残留的长事务卡在LOCK WAIT状态 - 想长期捕获,必须开
innodb_print_all_deadlocks = ON,日志会写进错误日志文件,而不是依赖这条命令
容易被忽略的上下文陷阱
死锁日志本身不告诉你 SQL 是怎么写的,只展示执行时的锁行为。比如你看到 index PRIMARY 被锁,但实际执行的可能是 UPDATE user SET status=1 WHERE name='xxx'——而 name 字段没索引,InnoDB 就被迫扫描主键并锁住一堆无关记录。
所以光看日志不够,必须同步做三件事:
- 用
EXPLAIN重放对应 SQL,确认是否走了预期索引 - 检查该表的索引定义,特别是 WHERE 和 JOIN 条件字段有没有被覆盖
- 注意隔离级别:
REPEATABLE READ下默认用 Next-Key Lock,READ COMMITTED会降级为 Record Lock,死锁概率显著降低但幻读风险上升
真正难的从来不是看懂日志,而是把锁行为和业务 SQL、索引设计、隔离级别这三层对齐。漏掉任何一层,都可能花两小时分析日志,最后发现只是少建了个联合索引。


















