SHOW ENGINE INNODB STATUS 在死锁发生时仅保留最近一次死锁的完整上下文,关键信息位于 LATEST DETECTED DEADLOCK 小节,含事务ID、表名、索引、锁模式及SQL语句,但不直接指出哪条SQL导致死锁。

死锁发生时 SHOW ENGINE INNODB STATUS 能看到什么
它不会直接告诉你“哪条 SQL 导致了死锁”,而是输出最近一次死锁的完整上下文,包括两个(或多个)事务各自持有的锁、等待的锁、执行的语句、事务 ID、线程 ID、加锁的行记录(含 PRIMARY 或索引名)、甚至加锁的 heap no。关键信息藏在 LATEST DETECTED DEADLOCK 小节里,不是开头那段长日志。
常见错误现象:翻完整个输出,只看到一堆 TRANSACTION 和 RECORD LOCKS,没找到自己业务里的表名或字段;或者误把 SEMAPHORES 或 TRANSACTIONS 小节当死锁现场。
- 真正要盯住的是以
*** (1) TRANSACTION:和*** (2) TRANSACTION:开头的两段 —— 它们代表死锁中被回滚的那个事务,和另一个被等待阻塞的事务 - 每段里
mysql tables in use和locked tables告诉你涉及哪些表;LOCK WAIT行说明当前在等什么锁;Trx has been waiting后面是等待时长 -
RECORD LOCKS space id后面的数字对应INFORMATION_SCHEMA.INNODB_SYS_TABLES里的SPACE,但更实用的是看它紧跟着的name字段,比如test/t1—— 这才是真实表名
如何快速定位到自己代码触发的那条 SQL
死锁现场的 SQL 是事务最后执行的语句,不一定是业务逻辑里最靠前的那条。InnoDB 只记录当前持有锁/等待锁的语句,且可能被截断(尤其带长字符串或 JSON 的时候)。所以不能只信 SQL 字段,得结合上下文反推。
使用场景:你改了某接口的更新逻辑,上线后监控报警说死锁陡增,但日志里没打全 SQL —— 这时就得靠 status 输出里的事务时间戳、线程 ID、表名、索引名交叉验证。
- 先记下死锁时间(
2024-05-22 14:23:18),再查 MySQL 错误日志或应用层慢查询日志,找同一秒附近、同一线程 ID(THREAD ID: 12345)的查询 - 注意
index `idx_status`这类提示 —— 如果你的 WHERE 条件没走这个索引,却在这里加了锁,大概率是用了OR、函数、类型隐式转换导致索引失效 - 如果看到
lock_mode X locks rec but not gap,说明是行锁;如果是lock_mode X locks gap before rec,说明有间隙锁 —— 后者更容易引发死锁,尤其在范围更新(如WHERE status IN (1,2))时
SHOW ENGINE INNODB STATUS 的时效性与刷新限制
它只保留最后一次死锁信息,且仅在发生死锁后才更新。没有死锁时,这部分内容是空的或停留在上次记录。这意味着:你反复执行它却看不到新内容,并不是命令失效,而是系统确实没再触发死锁。
性能 / 兼容性影响:该命令本身开销极小,但频繁调用(比如每秒刷一次)会增加 performance_schema 的采样压力,尤其在高并发写入场景下。MySQL 8.0+ 默认开启 innodb_status_output_locks,但老版本需手动设为 ON 才能看到详细锁信息。
- 别依赖它做实时监控 —— 应配合
information_schema.INNODB_TRX+INNODB_LOCK_WAITS做主动巡检 - 若发现
LATEST DETECTED DEADLOCK长期不更新,但应用报Deadlock found when trying to get lock,说明死锁被客户端捕获并重试了,InnoDB 没把它当作“最新一次”来记录(因为事务已回滚退出) - 某些云数据库(如阿里云 RDS)会屏蔽部分敏感字段(如具体行值),此时只能靠索引名和锁模式推测行为
为什么按固定顺序访问表和索引能减少死锁
死锁本质是循环等待:事务 A 持有 t1 锁等待 t2,事务 B 持有 t2 锁等待 t1。只要所有事务都按相同顺序(比如总是先更新 orders 再更新 order_items,且 WHERE 条件都走 PRIMARY KEY)操作资源,就不可能构成环。
容易踩的坑:你以为顺序一致,其实底层加锁顺序被优化器打乱了。比如 UPDATE t1 JOIN t2 ON ... SET t1.x=1,InnoDB 实际加锁顺序取决于驱动表和索引选择,未必是你写的表顺序。
- 显式用
SELECT ... FOR UPDATE提前加锁时,务必保证多表查询的FROM表顺序统一,且关联字段必须有索引,否则可能退化为全表扫描加锁 - 避免在同一个事务里混合使用主键更新和二级索引更新 —— 比如先
UPDATE t SET status=1 WHERE id=100,再UPDATE t SET score=99 WHERE status=1 AND type='A',后者走idx_status_type,锁范围不可控 - 批量更新时慎用
IN列表,特别是列表顺序不固定(如前端传参乱序)。改成按主键排序后再拼 SQL,或拆成多次单行更新
死锁不是配置调大就能消失的问题,它暴露的是并发路径上的资源争抢设计缺陷。看到 SHOW ENGINE INNODB STATUS 里反复出现同一对表、同一组索引,基本可以确定是业务逻辑里某个更新链路没对齐访问顺序。


















