必须立刻执行 show engine innodb status\g,因死锁日志仅驻留内存且只保留最近一次;定位关键段需搜索“latest detected deadlock”,其下(1)(2)事务块中holds和waiting行交叉比对可还原死锁环,sql、索引、锁模式与执行计划须三者对齐。

必须立刻执行 SHOW ENGINE INNODB STATUS\G,死锁日志只驻留在内存中且仅保留最近一次,稍有延迟就会被覆盖——这不是“建议”,是硬性前提。
怎么快速定位 LATEST DETECTED DEADLOCK 区域
整个命令输出很长,但真正有用的只有一页:搜索 ------------------------ LATEST DETECTED DEADLOCK ------------------------,直接跳转到该段开头。它不是日志时间线,而是两个事务互锁的快照,编号 (1) 和 (2) 不代表执行先后,只是标记区分。
- 如果没搜到这一段,说明:最近没发生死锁;或刚发生就被新死锁覆盖;或 MySQL 重启过(该信息不持久)
- 别在
TRANSACTIONS或SEMAPHORES部分反复翻找——那些反映的是当前状态,和已回滚的死锁无关 - 使用
\G格式化输出能避免文本挤成一行,方便逐行阅读
如何从日志里提取关键 SQL 和锁关系
重点盯住每个事务块里的两行:HOLDS THE LOCK(S) 和 WAITING FOR THIS LOCK TO BE GRANTED。它们必须交叉比对,才能还原死锁环。
- 在
*** (1) TRANSACTION:块中,向下找query id行(不是MySQL thread id),它后面跟着的就是正在执行的原始 SQL,例如:update users set status = 2 where id = 105 - 看同一事务块里的
HOLDS THE LOCK(S):它告诉你这个事务手里攥着什么资源,比如index PRIMARY of table `test`.`users`+lock_mode X locks rec but not gap,说明它持有一条主键记录的排他锁 - 再看
*** (2) TRANSACTION:块里的WAITING FOR THIS LOCK TO BE GRANTED:如果它等的正是上面那个索引和页号,就构成闭环 - 注意
lock_mode X locks gap before rec这类描述——这是间隙锁,常见于WHERE a > 5或唯一键冲突检查,比行锁更隐蔽、更容易引发并发死锁
为什么看到 SQL 却还是没法复现或修复
日志里的 SQL 是事务“最后执行的一句”,但它是否真走索引、是否触发了隐式锁升级,得结合执行计划和表结构判断。
- 如果日志显示锁在
index `idx_email`上,但你的业务 SQL 是WHERE name = ?,大概率是索引失效(比如字段类型不匹配、用了函数、或OR导致全表扫描) - 没定义主键的表,InnoDB 会建隐式聚簇索引
GEN_CLUST_INDEX,日志里出现这个名,别慌,去查SHOW CREATE TABLE确认 -
INSERT INTO ... SELECT场景下,WAITING FOR ... insert intention是典型信号——两个事务在抢同一段间隙,不是数据冲突,是插入并发控制机制本身在起作用 - 事务状态显示
ROLLING BACK?那是 InnoDB 已经完成的动作,你看到的日志是“回滚前最后一刻”的锁快照,不是正在卡住
最难的从来不是找到日志,而是理解为什么那条 UPDATE 会锁住本不该锁的范围——这需要把日志里的索引名、锁模式、page no 和实际执行计划三者对齐,缺一不可。











