SHOW ENGINE INNODB STATUS仅保留最近一次死锁日志且不落盘,必须立即执行并关注LATEST DETECTED DEADLOCK段,通过对比两个事务的HOLDS和WAITING锁信息定位闭环,结合SQL执行计划与业务加锁顺序分析根因。

死锁不是性能问题,而是事务并发冲突的必然结果;SHOW ENGINE INNODB STATUS 不能“定位性能死锁”,它只负责还原最近一次死锁的完整环路——你看到的不是慢,是卡死。
为什么执行 SHOW ENGINE INNODB STATUS\G 必须立刻做?
死锁日志只驻留在内存中,且仅保留最近一次。MySQL 不写盘、不归档、不缓存历史,新死锁一发生,旧记录就被覆盖。哪怕延迟 2 秒再执行,就大概率看不到任何 LATEST DETECTED DEADLOCK 段落。
- 没搜到该段?不是命令错了,而是:刚发生的死锁已被覆盖,或根本没发生死锁(可能是锁等待超时)
-
\G不可省略——它把长文本按字段换行,否则所有锁信息挤成一行,根本没法比对HOLDS THE LOCK(S)和WAITING FOR THIS LOCK TO BE GRANTED - 别在
TRANSACTIONS或SEMAPHORES部分反复翻找——那些反映当前活跃事务和信号量状态,和已回滚的死锁无关
怎么从输出里一眼揪出死锁关键链?
全文只盯一个区域:LATEST DETECTED DEADLOCK。它下面只有两块事务描述,编号 (1) 和 (2) 不代表执行先后,只是标记区分。
- 每个事务块里,先找
mysql thread id后面紧跟着的原始 SQL,比如update orders set status=2 where id=105—— 这才是应用真正执行的语句,不是 ORM 别名 - 再看同一事务里的
HOLDS THE LOCK(S)行:它告诉你这个事务手里攥着什么,例如index `idx_user_id` of table `app`.`orders`,配合lock_mode X locks rec but not gap说明是主键精确更新 - 然后跳到另一个事务块,找
WAITING FOR THIS LOCK TO BE GRANTED:如果它等的索引名、页号(page no 7)、甚至 heap no 都和前一个事务的HOLDS完全匹配,闭环就成立了 - 特别注意
lock_mode X locks gap before rec—— 这是间隙锁,常见于WHERE status IN (1,2)或无主键表上的隐式聚簇索引GEN_CLUST_INDEX,比行锁更隐蔽、更容易引发不可见循环
查到了 SQL 却修不好?多半是索引没走对
日志里的 SQL 是事务“最后执行的一句”,但它是否真命中索引、有没有触发隐式锁升级,得靠执行计划反推。
- 日志显示锁在
index `idx_email`上,但你的业务 SQL 是WHERE name = ??那基本是索引失效:字段类型不匹配、用了函数(如UPPER(email))、或OR导致全表扫描 - 没定义主键的表,InnoDB 会建隐式聚簇索引
GEN_CLUST_INDEX,日志里出现这个名,别慌,先跑SHOW CREATE TABLE确认结构 -
INSERT INTO SELECT场景下,WAITING FOR insert intention是典型信号——两个并发插入正抢同一段空隙,不是数据冲突,是间隙分配竞争
真正难的不是看懂日志,而是把 HOLDS 和 WAITING 对上号后,确认那两条 SQL 在业务逻辑里是否真的存在交叉加锁顺序;很多团队花几小时读日志,却漏掉一句“我们接口 A 先更新用户再更新订单,接口 B 反过来”,这才是根因所在。



















