必须开启innodb_print_all_deadlocks=on并配合高频轮询show engine innodb status及应用层异常上报,才能完整捕获死锁现场。

死锁日志只在 innodb_print_all_deadlocks=ON 时才写入 error log
MySQL 默认不记录所有死锁,只在发生死锁并回滚事务时记一条摘要到 error log(比如 Deadlock found when trying to get lock),但这条日志不含完整事务上下文、SQL、锁等待链。想看到“瞬间发生又立刻恢复”的死锁现场,必须开启全量死锁记录:innodb_print_all_deadlocks=ON。它不会影响性能,但会把每次死锁的完整信息(包括每个事务持有的锁、等待的锁、最后执行的 SQL、事务 ID、线程 ID)都打到 error log 里。
注意:innodb_print_all_deadlocks 是动态变量,可在线开启:SET GLOBAL innodb_print_all_deadlocks = ON;
但需确保 error log 路径可写、日志轮转策略合理,否则可能撑爆磁盘。
仅靠 error log 不够:需配合 SHOW ENGINE INNODB STATUS 的实时快照
死锁发生后,SHOW ENGINE INNODB STATUS\G 输出的 LATEST DETECTED DEADLOCK 区块会保留最近一次死锁的详细现场,但它会被下一次死锁覆盖——所以“瞬间恢复”场景下,你很可能还没来得及执行命令,现场就丢了。
- 必须用脚本高频轮询捕获:比如每 100ms 执行一次
SHOW ENGINE INNODB STATUS并提取LATEST DETECTED DEADLOCK块,写入独立文件 - 避免直接重定向整个输出,因为内容庞大且含无关信息;建议用
awk或 Python 正则精准提取 - 注意并发执行
SHOW ENGINE INNODB STATUS本身极轻量,但频繁调用仍需控制频率,100–500ms 是较安全区间
应用层主动上报是唯一能关联业务逻辑的方式
error log 和 SHOW ENGINE INNODB STATUS 都不带业务上下文:不知道哪个服务、哪个 API、哪个用户触发了该死锁。真正要定位“为什么这里会死锁”,必须让应用在捕获到 Deadlock found when trying to get lock 异常时,立即上报:
- 当前执行的完整 SQL(含参数值,注意脱敏)
- 调用栈(至少到 Controller 层)
- 事务开始时间、当前连接 ID(
CONNECTION_ID())、thread_id - 若用连接池(如 HikariCP),附上 pool 名和 acquisition stack trace
光靠数据库侧日志,永远只能看到“谁锁了什么”,看不到“谁在什么时候、为什么按这个顺序访问这些行”。
不要依赖 information_schema.INNODB_TRX + INNODB_LOCKS(MySQL 8.0.1+ 已移除)
老教程常提查 information_schema.INNODB_LOCKS 和 INNODB_LOCK_WAITS 来观察锁等待,但这两个表在 MySQL 8.0.1 中已被移除。现在唯一可靠的运行时锁视图是 performance_schema.data_locks 和 data_lock_waits,但它们默认关闭,且采样是异步的——死锁发生时,这些表未必及时刷新,更无法回溯已结束的死锁。
试图用 SELECT * FROM performance_schema.data_lock_waits 抓“正在发生的死锁”基本无效:死锁检测器一介入,冲突事务就被终止,锁记录随即清理,查询大概率返回空。
死锁现场稍纵即逝,关键不是堆工具,而是明确三件事:日志开关是否打开、是否有进程在抢拍 status 快照、应用是否在异常点埋了上下文。漏掉任意一环,就只剩猜。











