先执行 SELECT @@innodb_print_all_deadlocks; 返回1才生效;若为0需用SUPER权限SET GLOBAL开启或配置my.cnf并重启,日志输出到error_log且需log_error_verbosity≥3。

怎么确认死锁日志开关是否生效
MySQL 默认不记录完整死锁信息,只在错误日志里写一句 Deadlock found when trying to get lock,根本看不出谁锁了谁、哪条 SQL 卡住了。关键看 innodb_print_all_deadlocks 是否为 ON,而不是只靠查 SHOW ENGINE INNODB STATUS——那个只能看到最近一次死锁,且刷新后就丢。
实操建议:
- 连上 MySQL 后直接执行
SELECT @@innodb_print_all_deadlocks;,返回1才算开;返回0或报错说明没生效 - 如果返回
0,别急着改配置文件,先检查是否用了SET GLOBAL临时开启:SET GLOBAL innodb_print_all_deadlocks = ON;(需 SUPER 权限) - 注意:该变量是动态的,但仅对新连接生效;已存在的连接不会自动继承,重启或重连才管用
- 持久化必须写进配置文件
my.cnf的[mysqld]段:innodb_print_all_deadlocks = ON,然后重启 mysqld(仅SET GLOBAL不够)
死锁日志实际长什么样、去哪找
开启后,每次死锁都会完整打印到 MySQL 错误日志(error_log),不是慢查询日志,也不是 general_log。格式固定:以 *** (1) TRANSACTION: 开头,包含事务 ID、持有锁、等待锁、SQL 语句、锁类型(如 RECORD LOCKS space id)、甚至行物理位置(heap no 5)。
常见错误现象:
- 明明开了
innodb_print_all_deadlocks,却在错误日志里搜不到TRANSACTION块 → 很可能错误日志路径不对,用SELECT @@log_error;确认真实路径 - 日志里只有
Deadlock found...一行,没有后续分析 → 变量没真正生效,或 MySQL 版本太低(innodb_print_all_deadlocks从 5.6.2 开始支持) - 应用报死锁,但错误日志空空如也 → 检查
log_error_verbosity是否 ≥ 3(MySQL 5.7+),否则死锁事件可能被过滤掉
为什么 SET GLOBAL 有时不生效
表面上执行成功,但错误日志还是没死锁详情,大概率是权限或作用域问题。这个变量虽然可动态设置,但受严格限制。
实操建议:
- 必须用拥有
SUPER权限的账号执行SET GLOBAL innodb_print_all_deadlocks = ON;,普通 DBA 权限不够 - 某些云数据库(如阿里云 RDS、腾讯云 CDB)禁用所有
SET GLOBAL,只能通过控制台参数模板修改,且需要重启实例 - MySQL 8.0.22+ 引入了
persist机制,可用SET PERSIST innodb_print_all_deadlocks = ON;写入mysqld-auto.cnf,避免重启丢失 —— 但前提是persisted_globals_load=ON(默认开) - 执行完
SET GLOBAL后,立刻用另一个会话执行SELECT @@innodb_print_all_deadlocks;验证,不要只信返回的Query OK
死锁日志里最该盯住的三行
日志内容多,但真正定位根因只需聚焦三处,其余都是干扰项。别被一堆 lock_mode X locks rec but not gap 绕晕。
重点关注:
-
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:→ 这个事务卡在哪条 SQL、等哪个索引、哪一行(看space id和page number) -
*** (2) HOLDS THE LOCK(S):→ 另一个事务已经占着什么锁(注意看是不是唯一索引 or 二级索引,锁范围差异极大) -
mysql tables in use 1, locked 1下面的LOCK WAIT 3 lock struct(s)→ 锁结构数量异常高(比如 >5),往往意味着扫描了太多行,索引没走好或条件没命中索引
容易被忽略的是:死锁日志里的 SQL 是「触发死锁那一刻」正在执行的语句,不一定是业务里最可疑的那条——它可能是被前面某条 UPDATE 拖累的无辜者。得顺着事务开始时间(TRANSACTION 4219234123)去查 general_log 或应用埋点,才能还原完整链路。











