必须秒级执行SHOW ENGINE INNODB STATUS\G查看LATEST DETECTED DEADLOCK区块,因MySQL仅保留最近一次死锁日志,新死锁发生即覆盖旧记录,错过即不可恢复。

立刻执行 SHOW ENGINE INNODB STATUS\G,只盯 LATEST DETECTED DEADLOCK 区块——其他全是干扰项,晚一秒可能就看不到现场。
为什么刚报错 Deadlock 就查不到日志?
MySQL 默认只保留最近一次死锁的完整上下文,新死锁一发生,旧记录立即被覆盖。不是命令失效,是设计如此。
- 你看到的
ERROR 1213 (40001): Deadlock found when trying to get lock是应用层抛出的,但 InnoDB 内部早已回滚并刷新了状态 - 执行
SHOW ENGINE INNODB STATUS后没找到LATEST DETECTED DEADLOCK,大概率是中间又发生了一次死锁,把你要的那条冲掉了 - 这个命令对连接无状态依赖,但必须在发现异常后 秒级响应;靠事后翻监控或等 DBA 上线基本来不及
怎么看懂 LATEST DETECTED DEADLOCK 里的关键字段?
死锁本质是两个事务互相卡住,必须对照看「谁在等」和「谁在占」,单看一边会误判。
-
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:事务 1 卡在哪条 SQL、等哪个索引(如index `idx_user_id`)、哪一页(page no 456)、什么锁类型(lock_mode X locks gap before rec表示间隙锁) -
*** (1) HOLDS THE LOCK(S):事务 1 自己已经持有哪些锁——注意它可能持着事务 2 正在等的锁 -
*** WE ROLL BACK TRANSACTION (1):MySQL 选事务 1 当牺牲者,不是因为它写得错,而是 undo log 更小;别把它当“罪魁祸首”去改 - 主键值藏在
PHYSICAL RECORD后面的十六进制字段里,比如0x000000000000007B对应十进制123,需手动换算
怎么让每次死锁都落盘,不再靠手速抢?
仅靠 SHOW ENGINE INNODB STATUS 做长期排查不现实,必须开启日志持久化。
- 临时生效:
SET GLOBAL innodb_print_all_deadlocks = ON(对新连接生效,旧连接不感知) - 永久生效:在
my.cnf的[mysqld]段加innodb_print_all_deadlocks = 1,然后重启或用 RDS 参数模板更新 - 确认是否生效:
SELECT @@innodb_print_all_deadlocks;返回1才算成功 - 日志路径由
log_error配置决定,常用命令快速提取:grep -A 20 "Deadlock found" /var/log/mysql/error.log - ⚠️ 注意磁盘空间:高频死锁时日志增长极快,必须配
logrotate或云服务轮转策略
容易被忽略的三个硬伤点
很多人花几小时分析日志,却栽在这几个地方:
- 误以为
WAITING FOR最多的事务就是源头——其实 MySQL 已经回滚它了,真正要盯的是它 为什么等 和另一个事务 凭什么不放 - 在 MySQL 8.0+ 上还查
INFORMATION_SCHEMA.INNODB_LOCKS,该表早在 8.0.1 被移除,应改用performance_schema.data_locks - 看到
lock_mode X locks gap before rec就慌,其实这是 RR 隔离级别下范围查询的正常行为,重点不是删掉它,而是统一两个事务的访问顺序(比如都先查主键再查二级索引)


















