直接查看LATEST DETECTED DEADLOCK即可定位死锁根因,关键字段包括两个冲突事务的trx_mysql_thread_id和trx_query、各自持有的锁(HOLDS THE LOCK(S))及等待的锁(WAITING FOR THIS LOCK TO BE GRANTED),结合lock_mode判断锁类型与风险点。

直接看 LATEST DETECTED DEADLOCK 就能定位死锁根因,不用翻错误日志或查 processlist。
怎么看 LATEST DETECTED DEADLOCK 的关键字段
执行 SHOW ENGINE INNODB STATUS\G 后,找到 LATEST DETECTED DEADLOCK 区块。重点盯三块:
-
*** (1) TRANSACTION:和*** (2) TRANSACTION:—— 两个冲突事务的快照,注意trx_mysql_thread_id(线程 ID)和trx_query(正在执行的 SQL) -
*** (1) HOLDS THE LOCK(S):—— 这个事务当前持有哪些锁(比如X locks rec but not gap行锁,或X locks gap before rec间隙锁) -
*** (2) WAITING FOR THIS LOCK TO BE GRANTED:—— 它正卡在等什么锁,而这个锁恰好被事务 (1) 持有
从 lock_mode 判断锁类型和风险点
日志里出现的 lock_mode 直接暴露问题性质:
-
X locks rec but not gap:纯行锁,大概率是多表更新顺序不一致导致的循环等待 -
X locks gap before rec或next-key lock:间隙锁,基本锁定是 RR 隔离级别下范围查询(如WHERE status > 0)但没走索引引发的 -
locks rec but not gap waiting在WAITING FOR行出现,说明它想加行锁但被堵住;如果同一行出现在HOLDS里,就是典型 A 等 B、B 等 A
WHERE 条件没走索引时死锁概率飙升
当 UPDATE 或 DELETE 的 WHERE 条件无法命中索引,InnoDB 会全表扫描并加大量行锁,极易与其他事务锁范围重叠:
- 用
EXPLAIN确认语句是否走了预期索引,重点看type是range还是ALL,key是否为空 - 联合索引要满足最左前缀,
WHERE a = ? AND b > ?能用上(a,b),但WHERE b > ?就用不上 -
ORDER BY + LIMIT类语句若没索引,也可能触发非预期锁范围扩展
INSERT ON DUPLICATE KEY UPDATE 是高频死锁来源
这个语句在并发下容易触发间隙锁+插入意向锁冲突,尤其在唯一键冲突频繁的场景:
- 它本质会先尝试插入,失败时转为更新;过程中会对插入位置加
insert intention lock,同时可能对相邻间隙加gap lock - 如果多个事务同时操作相邻主键值(如自增 ID 连续写入),极易形成间隙锁交叉
- 替代方案可考虑先
SELECT ... FOR UPDATE显式加锁再判断,或改用REPLACE INTO(注意影响行数逻辑)
真正难的不是看懂日志,而是把 trx_query 和 lock_mode 对应到业务代码里的具体 SQL 执行路径——同一个接口可能在不同分支走不同索引,得结合调用栈和参数一起看。


















