SHOW ENGINE INNODB STATUS仅返回最近一次死锁的完整快照,位于LATEST DETECTED DEADLOCK区块,含事务ID、HOLDS/WAITING锁信息及被回滚事务;历史死锁会被覆盖,需配合innodb_print_all_deadlocks=ON写入错误日志方可追溯。

SHOW ENGINE INNODB STATUS 能看到什么
它只返回最近一次死锁的完整上下文,不是历史记录也不是实时流。关键信息全在 LATEST DETECTED DEADLOCK 区块里,包括两个事务的 SQL、各自持有的锁(HOLDS THE LOCK(S))、等待的锁(WAITING FOR THIS LOCK TO BE GRANTED),以及最终被回滚的是哪个事务(WE ROLL BACK TRANSACTION (X))。
常见误区是以为执行一次就能捕获所有死锁——其实只要中间发生过另一次死锁,前一次就被覆盖了。生产环境必须配合其他方式补位。
为什么 innodb_print_all_deadlocks=ON 是刚需
默认情况下 MySQL 只记最后一次,等于把黑匣子设成“覆盖式录音”。开启后,所有死锁都会追加写入错误日志(log_error 指定路径),便于事后回溯和统计频次。
- 临时开启:
SET GLOBAL innodb_print_all_deadlocks = ON; - 永久生效:在
my.cnf的[mysqld]段落加innodb_print_all_deadlocks = 1 - 验证是否生效:
SHOW VARIABLES LIKE 'innodb_print_all_deadlocks';返回ON或1
注意:该变量不支持会话级设置,只能全局生效;且开启后日志体积会增大,但远小于锁等待堆积带来的风险。
performance_schema.data_lock_waits 怎么查实时阻塞
MySQL 8.0+ 提供了更细粒度的锁等待视图,比 INNODB_LOCK_WAITS 更可靠(后者在高并发下可能为空)。它能直接关联等待方与持有方的线程 ID 和 SQL:
SELECT r.trx_id AS waiting_trx_id, r.trx_query AS waiting_query, b.trx_id AS blocking_trx_id, b.trx_query AS blocking_query FROM performance_schema.data_lock_waits w JOIN information_schema.INNODB_TRX r ON r.trx_id = w.WAITING_TRX_ID JOIN information_schema.INNODB_TRX b ON b.trx_id = w.BLOCKING_TRX_ID;
这个查询不依赖死锁是否已触发,只要存在锁等待(哪怕还没升级为死锁),就能捞出来。适合做监控告警的底表。
死锁日志里最易看错的三处
日志字段密集、缩进混乱,人工阅读容易跳行漏判:
-
*** (1) TRANSACTION和*** (2) TRANSACTION不代表执行先后顺序,只表示解析时的编号;真正顺序得看ACTIVE X sec时间戳 -
lock_mode X locks rec but not gap表示记录锁(非间隙锁),而lock_mode X locks gap before rec才是间隙锁——后者更容易引发连锁等待 -
space id和page no对应具体数据页,可用于结合INFORMATION_SCHEMA.INNODB_SYS_TABLESPACES定位哪张表哪条索引,但多数场景只需关注table `db`.`tbl`和index PRIMARY这类显式标识
真正难的不是读日志,而是把两条 SQL 放回业务逻辑里,还原出它们为何以相反顺序访问相同资源——那才是根因所在。


















