mysql默认死锁日志仅记录被回滚事务,开启innodb_print_all_deadlocks后可完整输出所有参与事务的锁信息、sql及堆栈,便于精准定位死锁根源,且无需重启即可动态启用。

死锁发生时 MySQL 默认只记录一个事务到错误日志
MySQL 的 InnoDB 引擎检测到死锁后,会自动回滚代价较小的事务,并在错误日志中写入一条 Deadlock found when trying to get lock。但默认只记录被选为 victim 的那个事务的 SQL 和事务 ID,另一个参与死锁的事务信息完全不输出——这导致你根本没法还原现场。
开启 innodb_print_all_deadlocks 后,每次死锁都会把**两个(或多个)事务的完整堆栈、持有的锁、等待的锁、SQL 语句**全打到错误日志里,是定位死锁根源的最低成本方式。
- 它不改变死锁处理逻辑(仍由 InnoDB 自动选 victim),只增强日志输出
- 仅影响错误日志(
error.log),不影响慢查询日志或 general log - 日志量增加有限:正常业务每小时可能就几条死锁,高频场景才需关注磁盘空间
怎么开启 innodb_print_all_deadlocks 并确认生效
这个变量是动态的,不需要重启 MySQL,但必须有 SUPER 权限。注意它只在当前实例生命周期内有效,重启后失效——所以得加进配置文件才真正“开启”。
- 运行时开启:
SET GLOBAL innodb_print_all_deadlocks = ON; - 永久生效:在
my.cnf的[mysqld]段落下这一行:innodb_print_all_deadlocks = ON - 验证是否生效:
SELECT @@innodb_print_all_deadlocks;返回 1 即成功 - 别漏掉日志路径检查:确保
log_error配置指向可写的文件,否则日志写不进去
死锁日志长什么样?关键字段怎么看
开启后,错误日志里会出现类似这样的块(删减版):
2024-05-22T10:32:14.123456Z 123 [Note] InnoDB: *** (1) TRANSACTION: TRANSACTION 123456789, ACTIVE 2 sec starting index read mysql tables in use 1, locked 1 LOCK WAIT 2 lock struct(s), heap size 1136, 1 row lock(s) MySQL thread id 123, OS thread handle 1402..., query id 456 localhost root updating UPDATE orders SET status = 'shipped' WHERE id = 12345 AND user_id = 789 <p>*** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 123 page no 456 n bits 72 lock_mode X locks rec but not gap Record lock, heap no 12 PHYSICAL RECORD: n_fields 5; compact format; info bits 0</p><p><strong><em> (2) TRANSACTION: TRANSACTION 123456790, ACTIVE 1 sec starting index read ... </em></strong> (2) HOLDS THE LOCK(S): ... *** WE ROLL BACK TRANSACTION (1)</p>
重点盯三块:TRANSACTION 后的 ID 和 SQL、HOLDS THE LOCK(S)、WE ROLL BACK TRANSACTION。对比两个事务各自持有什么锁、等待什么锁,就能看出循环依赖在哪一行索引上。
光开日志不够,还得配合监控和代码层防御
日志只是事后分析工具。死锁本身无法“自动处理”——MySQL 只能回滚一个事务,应用层必须能捕获 Deadlock found when trying to get lock 这个错误码(1213),并重试逻辑。
- 客户端收到 1213 错误时,不能直接报错给用户,应做指数退避重试(最多 3 次)
- 避免在事务里混用不同顺序访问多张表(比如 A→B 和 B→A),这是最常见死锁成因
-
SELECT ... FOR UPDATE尽量加在事务靠前位置,减少持有锁的时间窗口 - 如果发现某类操作频繁死锁,考虑用
INSERT ... ON DUPLICATE KEY UPDATE替代先查后更
日志开了,重试写了,访问顺序理清了——这时候再看到死锁日志,基本就是偶发边界问题,而不是系统性风险。











