必须第一时间执行SHOW ENGINE INNODB STATUS\G,因为死锁日志仅驻留内存且只保留最近一次,后续死锁会覆盖前次记录;该命令直接提供完整双事务快照,含互锁SQL、被回滚方、索引名及锁类型,无需依赖error.log或重启MySQL。

直接看 SHOW ENGINE INNODB STATUS\G 输出里的 LATEST DETECTED DEADLOCK 段,就能定位到哪两条 SQL 互锁、谁被回滚、锁在什么索引上——这是最准也最快的方式,不需要查 error.log 或重启 MySQL。
为什么必须第一时间执行 SHOW ENGINE INNODB STATUS\G
死锁日志只保留在内存中,且仅保留最近一次;下一次死锁发生时,前一次记录就被覆盖。你看到的报错 Deadlock found when trying to get lock 只是“结果”,真正能还原现场的只有这段输出。它不依赖日志文件路径是否可写,也不受 innodb_print_all_deadlocks 是否开启影响。
常见错误现象:
- 开发说“我只看到 ROLLBACK,但不知道哪条 SQL 触发的”——没查这个命令,就永远缺关键上下文
- 运维查
error.log发现只有一行错误,没事务2信息——因为默认不打全量,而INNODB STATUS始终有完整双事务快照
怎么从输出里快速抓出关键信息
整个输出很长,但只需盯住三块:
- 搜索
------------------------ LATEST DETECTED DEADLOCK ------------------------,直接跳到该段开头 - 找
*** (1) TRANSACTION和*** (2) TRANSACTION下的query id行,提取出正在执行的 SQL(如update t set c = c + 1 where id = 2) - 分别看两段里的
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED,确认锁类型(RECORD LOCKS/gap/next-key)、索引名(如index PRIMARY of table `test`.`t`)和锁模式(lock_mode X是排他锁)
注意:query id 行不一定紧贴事务头,有时隔几行;别只抄 MySQL thread id,那只是线程号,不是 SQL 本身。
SHOW ENGINE INNODB STATUS 的局限与补救
它只记录最近一次死锁,高频场景下容易错过。如果线上频繁死锁又抓不到现场,说明靠人工查太被动:
- 必须开启
innodb_print_all_deadlocks = ON(动态 SET 或写入 my.cnf),让每次死锁都落盘到log_error文件 - 开启后仍要定期检查
log_error路径权限和磁盘空间,否则日志写失败就等于没开 - 不能替代锁分析:光知道 SQL 不够,得结合执行计划判断是否走索引、是否触发间隙锁——比如
select ... where id = ? for update在主键不存在时会锁 (prev, next] 区间,两个事务同时插相邻 ID 就可能互等
真正难的从来不是“怎么查”,而是“查出来之后,怎么判断那条 insert 为什么锁了不该锁的范围”。间隙锁、临键锁、唯一索引 vs 普通索引的加锁差异,这些不会出现在 INNODB STATUS 的文本里,得靠你对 InnoDB 锁机制的理解去补全。


















