sys.schema_index_statistics无法定位死锁根源,因其仅统计索引读写频次,不记录事务ID、锁类型、等待关系;需查performance_schema.data_locks、data_lock_waits和events_waits_current三张表,并注意sys.innodb_lock_waits可能引发阻塞。

为什么开启sys.schema_index_statistics还抓不到死锁根源
因为sys.schema_index_statistics只统计索引的读写频次,不记录锁行为本身——它告诉你“哪个索引被扫得多”,但不告诉你“哪条SQL在什么条件下加了什么锁、持有多久”。死锁发生在事务执行过程中,而这个视图连事务ID、锁类型、等待关系都不包含,纯属事后归因工具,不是实时锁观测手段。
真正该查的performance_schema三张表
MySQL 8.0废弃了INNODB_LOCKS/INNODB_LOCK_WAITS,必须转向performance_schema。但默认不启用,容易漏掉关键数据:
-
data_locks:当前所有行级锁详情,含LOCK_MODE(如X,REC_NOT_GAP)、LOCK_DATA(具体锁定的主键值或间隙范围) -
data_lock_waits:谁在等谁,字段BLOCKING_TRX_ID和REQUESTING_TRX_ID直接对应死锁日志里的事务ID -
events_waits_current:需先开启消费者:UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_current';
漏掉任意一张,就无法拼出完整等待链。比如只查data_locks看到一堆X锁,但不知道谁在等谁,等于只见树木不见森林。
sys.innodb_lock_waits在8.0里反而会引发阻塞
这个视图看起来方便,但底层依赖performance_schema.data_locks和data_lock_waits,而这两张表在查询时会申请MDL锁。一旦有DDL(比如ALTER TABLE)正在执行,SELECT * FROM sys.innodb_lock_waits就会卡住,拖慢整个连接池响应,间接拉长锁等待时间,让原本不会死锁的场景也触发死锁。
- 线上监控脚本如果定期轮询这个视图,建议改用直接查
performance_schema.data_lock_waits并加超时 - 临时排查时,优先用
SHOW ENGINE INNODB STATUS\G看LATEST DETECTED DEADLOCK,它不走MDL,更轻量 - 确认是否真有DDL并发:查
SELECT * FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING';
从死锁日志反推加锁资源的关键字段
拿到SHOW ENGINE INNODB STATUS\G输出后,重点盯这几个字段,它们直接暴露加锁资源错位点:
-
*** (1) HOLDS THE LOCK(S):事务A实际持有的锁,注意space id和page no是否指向同一张表不同索引 -
*** (1) WAITING FOR THIS LOCK TO BE GRANTED:事务A在等什么?若index PRIMARY和index idx_status交替出现,说明SQL用了不同索引路径 -
lock_mode值为X, GAP时,代表间隙锁——常见于WHERE status IN ('pending', 'processing')这种范围条件,没走索引就全表扫描,锁住整个间隙 - 对比两个事务的
trx_query,看WHERE条件顺序是否一致,比如一个写WHERE user_id=100 AND order_time > '2026-05-01',另一个写WHERE order_time > '2026-05-01' AND user_id=100,优化器可能选不同索引
真正难定位的不是“有没有锁”,而是“为什么锁了不该锁的资源”——这往往藏在执行计划和索引选择里,不是靠查表能直接看到的。


















