必须看「LATEST DETECTED DEADLOCK」、「TRANSACTIONS」、「FILE I/O」三段,其中「LATEST DETECTED DEADLOCK」最关键,记录最近一次死锁中两个事务的持锁、等待锁及SQL详情,仅保留最后一次且易被覆盖。

SHOW ENGINE INNODB STATUS 输出里哪几块必须看
死锁发生后,SHOW ENGINE INNODB STATUS 是唯一能拿到现场快照的命令,但它输出很长,真正有用的就三段:「LATEST DETECTED DEADLOCK」、「TRANSACTIONS」、「FILE I/O」。其他全是干扰项。
重点只盯 LATEST DETECTED DEADLOCK —— 这里会完整记录最近一次死锁的两个事务各自持有哪些锁、等待什么锁、SQL 语句是什么。注意它只保留最后一次,不是历史日志。
常见错误是只扫一眼就关掉,结果漏掉关键行:*** (1) WAITING FOR THIS LOCK TO BE GRANTED: 和 *** (2) HOLDS THE LOCK(S): 才是定位冲突根源的核心。
为什么有时候执行 SHOW ENGINE INNODB STATUS 看不到死锁信息
不是命令失效,而是死锁早已过去,或者根本没触发 InnoDB 的自动检测机制。
- 死锁检测有窗口期:事务必须在等待锁时被 InnoDB 检测到循环依赖,如果其中一个事务超时回滚(
innodb_lock_wait_timeout默认 50 秒),就不会留下死锁记录 -
SHOW ENGINE INNODB STATUS只显示最近一次,高频死锁下容易被覆盖 - 某些场景下死锁被应用层捕获并重试了,但 DBA 没及时查,等想起来时记录已刷新
所以不能等出问题再查,得配合监控:用脚本定期抓取 SHOW ENGINE INNODB STATUS 输出,或监听 MySQL 错误日志里的 Deadlock found when trying to get lock。
从死锁日志里还原 SQL 和表结构的关键线索
日志里不会直接写 UPDATE users SET status = 1 WHERE id = 123,而是用类似 RECORD LOCKS space id 123 page no 123 n bits 72 这种底层描述。但你能从中提取出足够定位的信息:
-
TABLE: `mydb`.`orders`→ 明确库名和表名 -
index `PRIMARY`或index `idx_user_id`→ 锁在哪条索引上,说明 SQL 涉及该索引字段 -
lock_mode X locks rec but not gap→ 行锁(非间隙锁),大概率是等主键或唯一索引上的精确匹配 -
lock_mode S locks gap before rec→ 间隙锁,往往出现在范围查询(如WHERE status < 5)或插入前的检查
示例片段:*** (1) HOLDS THE LOCK(S): RECORD LOCKS space id 42 page no 102 n bits 72 index `idx_order_time` of table `mydb`.`orders` → 说明事务 1 正拿着 idx_order_time 上某几行的 X 锁,而事务 2 正卡在这条索引上等锁。
死锁日志里 transaction id 和 thread id 怎么对应到真实连接
日志里写的 TRANSACTION 4294967296 是 InnoDB 内部事务 ID,和 PROCESSLIST 里的 ID 不是一回事;thread id 12345 才是 SHOW PROCESSLIST 中的 ID 字段值。
所以要关联上下文,得靠这个组合:
- 先记下死锁日志中的
thread id - 立刻执行
SELECT * FROM information_schema.PROCESSLIST WHERE ID = 12345(把 12345 替换成实际值) - 看
INFO列是否还有未完成的语句,TIME是否为 0(刚卡住)或很小 - 如果
INFO为空,说明事务已回滚,但线程还连着——这时候要看COMMAND是Sleep还是Query
别指望 TRANSACTION ID 能直接查到 SQL,InnoDB 不对外暴露事务 ID 到连接映射表。真正能对上的只有 thread id。
死锁现场转瞬即逝,SHOW ENGINE INNODB STATUS 是快照不是录像。最常被忽略的是:它不告诉你“谁最先发起”,只告诉你“谁最后被选为牺牲者”;而那个被 kill 的事务,往往不是问题源头,只是锁等待链末端最轻的那个。


















