查不到data_lock_waits主因是performance_schema.setup_instruments中wait/lock/innodb%类采集器默认disabled,需手动启用enabled和timed;mysql 8.0.30+支持动态开启,旧版本需配置my.cnf并重启。

performance_schema.setup_instruments没开,查不到任何锁等待
很多排查失败的起点不是SQL写错,而是performance_schema根本没采集锁相关的事件。MySQL默认开启performance_schema,但wait/lock/innoDB%这类instrument默认是DISABLED。
执行这条语句确认状态:SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'wait/lock/innoDB%';
如果ENABLED或Timed列是NO,说明锁等待数据压根不会写入data_lock_waits表。
- MySQL 5.7–8.0.29:需在
my.cnf中加performance_schema_instrument = 'wait/lock/innoDB%=ON',然后重启mysqld - MySQL 8.0.30+:支持动态启用,运行
UPDATE performance_schema.setup_instruments SET ENABLED='YES', TIMED='YES' WHERE NAME = 'wait/lock/innoDB/row_lock'; - 别只开
wait/lock/innoDB%,顺手把wait/synch/mutex/innoDB%也开了,方便后续排查MDL或内存争用
data_lock_waits返回空,但SHOW PROCESSLIST显示Waiting for row lock
data_lock_waits只记录“当前活跃的等待关系”,一旦事务提交、回滚或超时释放锁,记录立刻消失。而SHOW PROCESSLIST里看到的Waiting for row lock可能发生在你查询data_lock_waits的间隙。
这时不能只查一次,要快速连查三张表形成证据链:
- 先用
SELECT THREAD_ID, PROCESSLIST_ID, PROCESSLIST_INFO FROM performance_schema.threads WHERE PROCESSLIST_STATE = 'Waiting for row lock';定位等待线程 - 再用
SELECT * FROM performance_schema.data_locks WHERE THREAD_ID = ?查它申请了什么锁 - 最后用
SELECT * FROM performance_schema.data_locks WHERE LOCK_TRX_ID = ?(从INNODB_TRX查到的trx_id)反查持锁者——因为data_lock_waits可能已清空,但data_locks仍保留当前所有活跃锁
注意:data_locks.LOCK_TRX_ID不是INNODB_TRX.trx_id,需通过performance_schema.threads关联PROCESSLIST_ID才能对应上真实会话。
LOCK_MODE和LOCK_DATA看不懂,误判间隙锁阻塞
LOCK_MODE字段值直接决定锁类型,必须对照理解:
-
S或X是共享/排他锁,REC_NOT_GAP表示只锁单行记录 -
GAP是纯间隙锁,不锁记录本身,只锁索引间隙(比如(10, 20)),常导致INSERT被堵住 -
NEXT_KEY是临键锁(记录锁 + 间隙锁),InnoDB默认RR隔离级别下UPDATE/DELETE走范围条件时最常见
LOCK_DATA才是关键线索:
- 显示
123:说明锁的是主键或唯一索引值为123的那行 - 显示
10, 20:说明是间隙锁,范围是(10, 20),新插入15会被阻塞 - 显示
NULL:大概率是锁了索引的 supremum 或 infimum 位置,典型于ORDER BY ... LIMIT未走索引时
如果LOCK_DATA为空或NULL,别急着下结论——先检查该SQL是否走了索引,EXPLAIN看key列是不是NULL,没走索引就容易升级成全表间隙锁。
持锁者SQL查不到,THREAD_ID对不上INNODB_TRX
data_lock_waits.BLOCKING_ENGINE_TRANSACTION_ID和INNODB_TRX.trx_id不是同一套ID体系,强行JOIN会丢失数据。正确路径是:
- 从
data_lock_waits.BLOCKING_ENGINE_TRANSACTION_ID→ 关联data_locks.ENGINE_TRANSACTION_ID→ 得到data_locks.THREAD_ID - 再用这个
THREAD_ID去performance_schema.threads查PROCESSLIST_ID和PROCESSLIST_INFO - 如果
PROCESSLIST_INFO为空(常见于空闲连接),说明持锁事务没在执行SQL,极可能是应用端开启了事务但忘了COMMIT或ROLLBACK
此时要立刻查INNODB_TRX里的trx_started时间,超过5分钟还没结束的事务,基本可以判定是代码缺陷或连接池异常——别指望它自己释放。
真正隐蔽的等待往往卡在这一步:锁还在,但持锁线程已“静默”,PROCESSLIST_INFO为空,data_locks却持续存在。这时候翻代码比翻SQL更有效。











