直接查看SHOW ENGINE INNODB STATUS\G输出中的LATEST DETECTED DEADLOCK区块即可准确定位死锁:它完整呈现两个事务的SQL、线程ID、持锁(HOLDS THE LOCK(S))与等待锁(WAITING FOR THIS LOCK TO BE GRANTED)关系、索引名及锁类型,是唯一实时、精准、零配置的排查入口。

直接看 SHOW ENGINE INNODB STATUS\G 输出里的 LATEST DETECTED DEADLOCK 区块,就能准确定位哪两条 SQL 互锁、谁被回滚、锁在哪个索引上——不需要查错误日志、也不用翻应用日志找报错堆栈。
怎么看 LATEST DETECTED DEADLOCK 里的关键字段
执行命令后,跳转到 LATEST DETECTED DEADLOCK 部分,重点盯三块:
-
*** (1) TRANSACTION:和*** (2) TRANSACTION:—— 分别是两个冲突事务的快照,记下trx_mysql_thread_id(即线程 ID)和trx_query(正在执行的 SQL),这就是导致死锁的两条语句 -
*** (1) HOLDS THE LOCK(S):—— 事务 1 当前持有哪些锁,比如X locks rec but not gap表示它正拿着某行的排他记录锁 -
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:—— 事务 2 正卡在等什么锁,而这个锁恰好被事务 1 持有
如果看到 gap before rec 或 next-key lock,基本可断定是 RR 隔离级别下范围查询 + 无索引引发的间隙锁交叉;如果全是 rec but not gap,更可能是多表更新顺序不一致导致。
WHERE 条件没走索引时怎么快速验证
死锁高发场景之一:UPDATE/DELETE 的 WHERE 条件未命中索引,InnoDB 退化为全表扫描并加大量行锁。排查方法如下:
- 对出问题的 SQL 执行
EXPLAIN,检查type是否为ALL(全表扫描)、key是否为空 - 联合索引要满足最左前缀,
WHERE a = ? AND b > ?能用上(a,b),但WHERE b > ?就用不上 - 注意隐式类型转换:比如字段是
VARCHAR,却传入数字,会导致索引失效 - ORDER BY + LIMIT 类语句若没索引,也可能触发非预期锁范围扩展
为什么不能只依赖错误日志查死锁
MySQL 默认只把最后一次死锁写进错误日志(log_error),高频死锁时会覆盖掉前面的现场。更可靠的做法是:
- 在
my.cnf中开启innodb_print_all_deadlocks = ON,让每次死锁都落盘 - 错误日志路径由
log_error参数指定,常见位置如/var/log/mysql/error.log - 但即便开了该参数,日志里仍只保留原始文本,没有结构化解析,不如
SHOW ENGINE INNODB STATUS直观、实时、带上下文 - 另外,
information_schema.INNODB_TRX、INNODB_LOCK_WAITS等表只能查“当前”锁等待,死锁发生后瞬间已结束,查不到历史快照
真正容易被忽略的是:死锁日志里 trx_query 显示的 SQL 是事务中“最后执行的一条”,但它未必是唯一加锁语句;前面的 SELECT ... FOR UPDATE 或 INSERT ON DUPLICATE KEY UPDATE 同样可能埋下锁冲突伏笔,需结合业务逻辑倒推完整事务流。

















