mysql默认不记录死锁历史,必须开启innodb_print_all_deadlocks=on才能获取完整可追溯的死锁日志,该日志追加至log_error指定的错误日志文件中,而非独立文件;show engine innodb status仅保留最后一次死锁快照,无法替代持久化日志。

MySQL 默认不记录死锁历史,必须显式开启 innodb_print_all_deadlocks 才能查到完整、可追溯的死锁记录。
死锁日志默认根本不存在
你执行 SHOW ENGINE INNODB STATUS\G 看到的 LATEST DETECTED DEADLOCK 是个单次快照,不是历史记录——它只保留最后一次,之前所有死锁全被覆盖。应用报了十几次 Deadlock found when trying to get lock,但日志里找不到上下文?大概率是 innodb_print_all_deadlocks 还关着。
-
SHOW VARIABLES LIKE 'innodb_print_all_deadlocks'返回OFF,就说明没开 - 这个开关默认就是
OFF,MySQL 不会主动写死锁详情到任何地方 - 开了之后,日志也不是独立文件,而是追加进 MySQL 的错误日志(
log_error指向的路径)
怎么确认并开启 innodb_print_all_deadlocks
分两步:先验证当前状态,再决定用临时还是永久方式开启。注意:临时设置重启即失效,生产环境必须走配置文件。
- 查当前值:
SELECT @@innodb_print_all_deadlocks;—— 返回1才算生效 - 临时开启(立即生效,但 MySQL 重启后还原):
SET GLOBAL innodb_print_all_deadlocks = ON; - 永久开启:编辑
my.cnf或mysqld.cnf,在[mysqld]段落下加一行:innodb_print_all_deadlocks = 1,然后重启或热加载(RDS 可走参数模板) - 别忘了确认
log_error路径可写:SHOW VARIABLES LIKE 'log_error';,再检查 MySQL 进程用户(如mysql)对该路径有写权限
怎么从错误日志里提取死锁记录
开了 innodb_print_all_deadlocks 后,每次死锁都会在错误日志里生成一段以 *** (1) TRANSACTION: 开头的块。直接 tail -f 瞪着看容易漏,得加缓冲控制。
- 实时监控命令(带行缓冲):
tail -f /var/log/mysql/error.log | grep --line-buffered -i "deadlock" - 查历史记录(比如最近 24 小时):
grep -A 30 -B 5 "LATEST DETECTED DEADLOCK" /var/log/mysql/error.log(注意:错误日志里实际写的是*** (1) TRANSACTION,不是这个字符串;但很多日志轮转后残留的旧SHOW ENGINE输出可能混在里面) - 真正有效的匹配模式是:
grep -A 50 -B 5 "RECORD LOCKS.*space id" /var/log/mysql/error.log—— 因为每段死锁日志必含RECORD LOCKS space id - 日志内容会被
logrotate切走,记得检查/etc/logrotate.d/mysql是否保留足够天数(至少 7 天)
为什么 SHOW ENGINE INNODB STATUS 不够用
它快、方便、不用配置,但本质是个“覆盖式缓存”,不是归档机制。线上偶发死锁,等你登录上去查,大概率已经刷新成下一次的,或者干脆没再发生过。
- 它只返回一个事务的完整 SQL,另一个事务通常只留 ID 和锁类型,关键上下文缺失
- 输出中
HOLDS THE LOCK(S)和WAITING FOR THIS LOCK必须成对看,但被覆盖后就断链了 - 依赖它做根因分析,容易把“最后执行的 SQL”当成原因,而忽略两个事务访问索引的顺序差异(比如一个先查
idx_account再改主键,另一个反过来) - 如果你用的是 RDS 或容器化部署,
SHOW ENGINE INNODB STATUS输出还可能被截断或脱敏,进一步丢失线索
真正要查历史,唯一可靠的方式就是让 innodb_print_all_deadlocks = ON 长期开着,并确保 log_error 文件不被静默丢弃——权限、路径、轮转策略,三者缺一不可。











