必须执行 SHOW ENGINE INNODB STATUS\G 定位 LATEST DETECTED DEADLOCK 区块,交叉比对两个事务的 HOLD 和 WAIT 锁信息,并结合末尾 query id 与业务映射;注意区分线程 ID 与事务 ID,间隙锁、隐式主键及 ORM 参数隐藏等常见陷阱。

必须立刻执行 SHOW ENGINE INNODB STATUS\G,否则死锁信息会被覆盖——它只驻留在内存中,且仅保留最近一次。
怎么快速定位 LATEST DETECTED DEADLOCK 区块
整段输出很长,但真正有用的只有一小块。执行命令后,立刻在终端里搜索:------------------------ LATEST DETECTED DEADLOCK ------------------------。别翻 TRANSACTIONS 或 SEMAPHORES 部分——那些反映的是当前状态,和已回滚的死锁无关。
- 搜不到该区块:说明最近没发生死锁,或刚发生就被新死锁覆盖,或 MySQL 重启过(该信息不持久)
- 用普通
;结尾执行,输出挤成一行:必须加\G格式化,否则根本没法读 - 连的是只读从库:
SHOW ENGINE INNODB STATUS在从库上不反映主库死锁,排查前先确认连接目标
怎么看懂 (1) 和 (2) 事务块里的锁关系
区块内有两个事务块,分别以 *** (1) TRANSACTION: 和 *** (2) TRANSACTION: 开头。编号不代表执行先后,只是标记区分。关键要交叉比对:
-
HOLDS THE LOCK(S):这个事务当前「攥着什么」——记下index `xxx`、space id、page no,它锁了哪张表、哪个索引、大概哪几行 -
WAITING FOR THIS LOCK TO BE GRANTED:这个事务「卡在哪」——它等的锁,必须和另一个事务的HOLDS对得上,才构成闭环 - 末尾的
query id行(如update users set status = 2 where id = 105):这是你能映射到业务逻辑的唯一锚点,务必抄下来
容易踩的坑:MySQL thread id 12 不是事务 ID,只是连接线程号;忽略 lock_mode X locks gap before rec 这类描述——这是间隙锁,常见于 WHERE a > 5 或唯一键冲突检查;看到 GEN_CLUST_INDEX 就懵——说明表没显式主键,InnoDB 用了隐式聚簇索引,赶紧查 SHOW CREATE TABLE。
为什么日志里的 SQL 看起来对不上业务代码
日志中显示的 SQL 是事务「最后卡住的那句」,不是第一句,更不是完整上下文。ORM(如 MyBatis)生成的预编译语句只显示 WHERE id = ?,参数值不会展开。
- 如果日志里锁在
index `idx_email`上,但你的业务 SQL 是WHERE name = ?,大概率是索引失效(字段类型不匹配、用了函数、或OR导致全表扫描) - 两个事务加锁顺序不一致:比如事务1先锁
idx_a再等idx_b,事务2先锁idx_b再等idx_a→ 典型死锁环 -
INSERT ... ON DUPLICATE KEY UPDATE在 RR 隔离级别下可能同时持 S 锁和 X 锁,尤其在唯一索引冲突检查时触发间隙锁竞争
结合 INFORMATION_SCHEMA.INNODB_TRX 辅助判断
INFORMATION_SCHEMA.INNODB_TRX 是死锁诊断首选表,需筛选 trx_state='LOCK WAIT' 并按 trx_started 升序排查;trx_state='RUNNING' 且 COMMAND='Sleep' 的未提交事务更危险。
- 执行
SELECT * FROM INFORMATION_SCHEMA.INNODB_TRX WHERE trx_state = 'LOCK WAIT' ORDER BY trx_started ASC—— 越早开始的越可能是“元凶”或长期未提交的休眠事务 -
INNODB_LOCK_WAITS在 5.7 中不可靠,常返回空或blocking_trx_id为空;此时必须回到SHOW ENGINE INNODB STATUS\G的TRANSACTIONS段落里找真实持有者 - 出现大量
lock_mode = 'GAP'或'INSERT_INTENTION',基本可断定是间隙锁竞争,尤其在无索引WHERE条件或并发INSERT场景下
最易被忽略的一点:死锁日志里出现的 lock_mode X locks rec but not gap 和 lock_mode X locks gap before rec 不是并列关系,而是同一把锁在不同阶段的表现;间隙锁本身不阻塞插入意向锁,但两者叠加会触发死锁判定——这点在 INSERT ... ON DUPLICATE KEY UPDATE 场景中反复验证过。


















