必须在my.cnf的[mysqld]段添加innodb_print_all_deadlocks = on并重启mysql才真正生效;动态set global仅对新连接临时有效且重启丢失,日志输出至log_error指定的错误日志文件,需确保路径可写且log_error_verbosity≥3。

如何开启MySQL 8.0的死锁日志记录
MySQL 8.0 默认不将死锁信息写入错误日志,必须显式启用 innodb_print_all_deadlocks 才能捕获完整死锁事件。仅靠 SHOW ENGINE INNODB STATUS 查看最近一次死锁是不可靠的——它会被后续任何死锁或状态查询覆盖。
操作步骤:
- 在
my.cnf的[mysqld]段中添加:innodb_print_all_deadlocks = ON - 重启 MySQL 或动态设置(需 SUPER 权限):
SET GLOBAL innodb_print_all_deadlocks = ON - 确认生效:
SELECT @@innodb_print_all_deadlocks应返回1
注意:该参数只影响 InnoDB 层面的死锁检测日志,不影响性能;但日志量取决于死锁频率,高并发业务需确保错误日志轮转策略合理(如用 logrotate 配合 mysqladmin flush-logs)。
如何从错误日志中提取结构化死锁信息
MySQL 将死锁详情以固定格式写入错误日志(非 slow log),每条记录以 Deadlock found when trying to get lock 开头,后跟事务、SQL、锁等待链等原始文本。直接 grep 解析脆弱且易漏(比如跨行、多事务嵌套)。
推荐做法:
- 用
awk或 Python 脚本按段落切分(以202[0-9]-[0-9]{2}-[0-9]{2}时间戳或*** (1)为段落边界) - 关键字段提取目标:
TRANSACTIONID、WAITING FOR锁类型、HOLDS THE LOCK(S)、被回滚的ROLLING BACK事务、以及最后出现的query行(即触发死锁的 SQL) - 避免只匹配第一行
Deadlock found...—— 它不包含 SQL,真正有用的是后续几行中带query关键字的那行
示例(日志片段):
2024-06-15T10:23:45.123456Z 12345 [ERROR] [MY-013183] [InnoDB] Deadlock found when trying to get lock... *** (1) TRANSACTION: TRANSACTION 123456789, ACTIVE 0 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) *** (1) WAITING FOR THIS LOCK TO BE GRANTED: RECORD LOCKS space id 123 page no 456 n bits 72 index PRIMARY of table `db`.`t` trx id 123456789 lock_mode X locks rec but not gap waiting *** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 123 page no 789 n bits 120 index idx_b of table `db`.`t` trx id 123456789 lock_mode S *** (1) WAITING FOR THIS LOCK TO BE GRANTED: ... *** (2) TRANSACTION: ... *** (2) ROLLING BACK ... *** WE ROLL BACK TRANSACTION (2) 2024-06-15T10:23:45.123457Z 12345 [ERROR] [MY-013184] [InnoDB] Transaction 123456789 was rolled back.
如何对接 Prometheus + Grafana 做死锁告警
MySQL 自身不提供死锁计数指标,需借助外部采集器解析错误日志并暴露为 Prometheus metrics。直接使用 mysqld_exporter 是无效的——它不解析错误日志,global_status 中的 Innodb_deadlocks 是累计值且无法区分时间窗口,也不含上下文。
可行路径:
- 用
mtail或filebeat + grok实时 tail 错误日志,匹配死锁模式,提取时间戳和事务ID,输出为mysql_deadlock_total{instance="xxx"} 1类型指标 - 在 Prometheus 中配置 recording rule,计算每分钟增量:
rate(mysql_deadlock_total[1m]) - Grafana 告警规则建议阈值:
rate(mysql_deadlock_total[5m]) > 0.2(即平均每 5 分钟超 1 次),避免单次抖动误报 - 告警消息里必须带上最新一条死锁的原始日志片段(通过
last_over_time(mysql_deadlock_log_raw[1h])类似方式关联),否则无法定位 SQL 和表
为什么不要依赖 INFORMATION_SCHEMA.INNODB_TRX 查看死锁
INFORMATION_SCHEMA.INNODB_TRX 只反映当前正在运行的事务,而死锁发生后,至少一个事务已被 InnoDB 强制回滚,此时它在该表中已消失。你查到的只是“幸存者”,不是冲突双方。
典型误判场景:
- 执行
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE TRX_STATE = 'LOCK WAIT',发现某事务卡住 —— 但它可能正等待一个早已被回滚事务释放的锁,实际无死锁 - 死锁刚发生时,两个事务都还在
INNODB_TRX中(短暂窗口),但这个时间极短(毫秒级),人工或定时脚本几乎不可能捕获 - 该表不记录锁对象(哪行、哪个索引)、不记录 SQL、不记录事务启动时间,对根因分析毫无帮助
真正有效的死锁诊断永远始于错误日志中的完整堆栈,而不是运行时视图。











