InnoDB死锁信息仅保留最近一次,需立即执行SHOW ENGINE INNODB STATUS查看;关键段落LATEST DETECTED DEADLOCK包含两事务的锁等待链、SQL、Trx id及线程ID;默认无时间戳,须开启innodb_print_all_deadlocks=ON才能持久化到error log。

怎么看 innodb_status 里的死锁信息
MySQL 的 SHOW ENGINE INNODB STATUS 输出中,死锁信息只保留在最近一次发生的死锁记录里,且每次新死锁会覆盖旧记录。所以发现死锁后要立刻执行该命令,否则堆栈就丢了。
关键段落是 LATEST DETECTED DEADLOCK,它下面会包含两个事务的完整锁等待链:谁持有什么锁、谁在等什么锁、SQL 语句是什么、事务 ID 和线程 ID(Trx id / MySQL thread id)。
注意:这个输出默认不带时间戳,如果没开 innodb_print_all_deadlocks=ON,历史死锁全靠运气抓。
为什么 SHOW ENGINE INNODB STATUS 看不到死锁
最常见原因是死锁刚发生就被自动回滚了,而你没及时查;或者 MySQL 配置里关掉了死锁日志持久化。
- 确认是否开启持久化记录:
SELECT @@innodb_print_all_deadlocks;—— 必须为ON才会写入 error log - 检查 error log 路径:
SELECT @@log_error;,然后用grep -A 20 "Deadlock found" /var/log/mysql/error.log搜 - 5.6+ 版本 error log 中的死锁记录比
INNODB STATUS更全,包含完整 SQL 和锁模式(如X locks rec but not gap)
从死锁堆栈识别典型冲突模式
死锁不是随机事件,多数来自可预测的访问顺序问题。看 LATEST DETECTED DEADLOCK 时重点抓三处:
- 两个事务的 SQL 语句顺序:比如事务 A 先更新
user再更新order,事务 B 反过来,就构成环路 - 锁类型和范围:
lock_mode X locks rec but not gap是行锁,lock_mode X locks gap before rec是间隙锁,后者容易因唯一键冲突或范围查询意外扩大 - 索引使用情况:看
index `idx_xxx` of table `db`.`t1`—— 如果走的是非唯一二级索引,InnoDB 会先锁二级索引再锁主键,增加锁粒度
示例片段中若看到两事务都在等 PRIMARY 上同一行,但持有不同二级索引锁,大概率是没走对索引或用了 OR/IN 导致扫描范围过大。
如何避免重复踩坑:加锁顺序 + 索引优化
死锁排查完不改代码,下次照撞。核心动作就两个:
- 统一 DML 操作的表/行访问顺序:比如所有业务都按
user → order → item顺序更新,别让服务 A 和服务 B 各自约定一套 - 确保 WHERE 条件命中唯一索引或覆盖索引:避免因
type: ALL或type: index触发全表/全索引扫描,把锁从一行扩到多行甚至整个索引树 - 减少事务内 SQL 数量:把
UPDATE拆成单条执行,比批量UPDATE ... IN (1,2,3)更可控;尤其避免在事务里调外部 HTTP 接口
真正难的不是看懂堆栈,而是还原出那几毫秒内两个事务的真实执行路径——应用层日志、SQL 慢日志(long_query_time=0)、以及 binlog 位置对齐,缺一不可。











