wait-for graph 是 innodb 内存中用于死锁检测的有向图,节点为活跃事务 id,边表示“事务 a 等待事务 b 持有的锁”,不持久化、不可直接查询,仅通过 show engine innodb status 中的死锁日志反推其局部快照。

Wait-for Graph 是什么,它在 InnoDB 里怎么存
Wait-for Graph 不是 MySQL 暴露给用户的配置项或视图,而是 InnoDB 内存中维护的一个运行时数据结构,用于死锁检测。它不落盘、不持久化,也不提供 SQL 接口直接查询。节点是活跃事务 ID(如 trx_id=12345),边是“事务 A 正在等待事务 B 持有的锁”,即一条 A → B 的有向边。
你无法用 SELECT 查它,但能通过 SHOW ENGINE INNODB STATUS 的 TRANSACTIONS 和 LATEST DETECTED DEADLOCK 区块反推它的局部快照——后者就是检测到环后截取的图片段。
死锁报错里看到的 “WAITING FOR THIS LOCK TO BE GRANTED” 怎么对应到 Wait-for Graph
每次事务因锁阻塞,InnoDB 就实时更新 Wait-for Graph:只要一个事务在 lock_wait_suspend_thread 状态下等待另一个事务释放锁,就会插入一条边。比如:
- 事务
trx_id=1001持有PRIMARY上 id=5 的 X 锁,正在执行UPDATE t SET v=1 WHERE id=5 - 事务
trx_id=1002执行UPDATE t SET v=2 WHERE id=5被阻塞,此时图中新增边:1002 → 1001 - 若
trx_id=1001又去等trx_id=1002持有的另一把锁(比如 id=8),边1001 → 1002也加入——环形成,死锁触发
所以日志里出现的 “waiting for this lock to be granted” 后面跟的事务 ID,就是图中箭头指向的目标节点;而当前事务 ID 就是箭头起点。
为什么有时死锁没被检测到,反而卡住直到 innodb_lock_wait_timeout
Wait-for Graph 检测不是全量实时扫描,而是按需 + 周期双触发:
- 每次加锁失败进入等待时,立即做一次局部环检测(只检查涉及该等待路径的子图)
- 同时后台每 10ms 执行一次全局图遍历(默认由
innodb_deadlock_detect控制,设为OFF会彻底禁用该机制)
如果 innodb_deadlock_detect=OFF,就只剩超时机制兜底,这时你会看到事务卡住 50 秒(默认 innodb_lock_wait_timeout=50)后报错 Lock wait timeout exceeded,而不是 Deadlock found。这不是图没建,是检测逻辑被关了。
从 SHOW ENGINE INNODB STATUS 看 Wait-for Graph 的实际输出
SHOW ENGINE INNODB STATUS 中的 LATEST DETECTED DEADLOCK 块,本质就是环检测成功后 dump 出来的 Wait-for Graph 子图。注意三类关键信息:
- 每个事务开头的
*** (1) TRANSACTION:后的TRANSACTION <code>123456, ACTIVE —— 这是图节点 -
mysql tables in use 1, locked 1和后续的LOCK WAIT—— 表明该节点正发出等待请求 -
WAITING FOR THIS LOCK TO BE GRANTED:后面的record lock和space id/page no—— 结合上一行的HELD LOCKS,可反推出边指向哪个事务
真正难的是跨事务拼接依赖链:日志不会直接写 “1001 → 1002 → 1001”,你需要人工对齐事务 ID、锁对象和持有/等待状态。这也是为什么线上一出死锁,第一反应不该是改代码,而是先看这块日志能不能还原出环。
Wait-for Graph 的关键不在“图多漂亮”,而在“边是否及时建立”和“环是否被准确识别”。最容易被忽略的点是:间隙锁(Gap Lock)参与成环时,SHOW ENGINE INNODB STATUS 很可能不显示具体记录,只写 supremum pseudo-record——这时候得结合 SQL 条件和索引结构,手动补全那条隐式边。











