应优先使用performance_schema.data_lock_waits查锁等待关系,前提是mysql≥5.7且已启用wait/lock/innodb相关instrument;该表需关联data_locks才能获取锁对象、模式及lock_data等关键信息,用于精准定位间隙锁阻塞等场景。

直接用 performance_schema.data_lock_waits 查当前谁在等谁,比翻 SHOW ENGINE INNODB STATUS 日志快得多,也比废弃的 information_schema.INNODB_LOCK_WAITS 更准——前提是 MySQL 版本 ≥ 5.7 且已启用相关 instrument。
确认 performance_schema 是否可用且已采集锁数据
很多问题其实卡在这一步:表存在但没数据,不是 SQL 写错,而是采集开关没开。
-
performance_schema默认开启,但锁相关的采集点(如wait/lock/innoDB)默认是关闭的 - 运行
SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'wait/lock/innoDB%';,检查ENABLED和Timed列是否为YES - 若为
NO,需在配置文件中加:performance_schema_instrument = 'wait/lock/innoDB%=ON',然后重启 mysqld(动态修改仅部分版本支持) - MySQL 8.0+ 中,
data_locks和data_lock_waits是实时表,但只记录“当前活跃锁”,不保留历史;事务提交/回滚后锁记录立即消失
查实时锁等待关系:用 data_lock_waits 关联 data_locks
data_lock_waits 只存等待关系 ID,不带 SQL 或线程信息;必须连查 data_locks 才能知道锁在哪张表、哪个索引、什么模式。
- 核心查询模板:
SELECT w.OBJECT_SCHEMA, w.OBJECT_NAME, w.INDEX_NAME, w.LOCK_TYPE, w.LOCK_MODE, w.LOCK_STATUS, w.BLOCKING_ENGINE_TRANSACTION_ID, w.REQUESTING_ENGINE_TRANSACTION_ID, d.LOCK_DATA FROM performance_schema.data_lock_waits w JOIN performance_schema.data_locks d ON w.REQUESTING_ENGINE_LOCK_ID = d.ENGINE_LOCK_ID;
-
LOCK_MODE值要会读:S是共享锁,X是排他锁,REC_NOT_GAP表示纯记录锁,GAP是间隙锁,NEXT_KEY是临键锁 -
LOCK_DATA字段显示被锁的具体值(如123)或范围(如10, 20),对间隙锁尤其关键——它告诉你“为什么 INSERT 被堵住了” - 注意:
BLOCKING_ENGINE_TRANSACTION_ID不等于INFORMATION_SCHEMA.INNODB_TRX.trx_id,需通过THREAD_ID关联performance_schema.threads找到对应会话和PROCESSLIST_ID
定位持锁者正在执行的 SQL
知道谁在等还不够,得知道阻塞方到底在干啥——它可能是个没提交的事务,也可能在执行一条慢 UPDATE。
- 先用
data_lock_waits.BLOCKING_ENGINE_TRANSACTION_ID关联performance_schema.threads得到THREAD_ID - 再关联
performance_schema.events_statements_current(非 history 表!)查最新语句:SELECT SQL_TEXT FROM performance_schema.events_statements_current WHERE THREAD_ID = ?;
- 如果
events_statements_current.SQL_TEXT为空,说明该线程当前没在执行语句(比如事务已开启但停在 COMMIT 前),此时要去查information_schema.INNODB_TRX的trx_started和trx_state,确认是否长时间 idle in transaction - 特别注意:
sys.innodb_lock_waits是视图,底层仍依赖performance_schema,但它把关联逻辑封装好了,适合快速看一眼;不过字段少、不可定制,排查复杂链路时不如手写 JOIN 灵活
间隙锁导致的 INSERT 阻塞容易被误判为“没锁”
这是最常踩的坑:data_lock_waits 里查不到等待记录,但业务确实卡在 INSERT 上——因为间隙锁本身不冲突,只有当另一个事务尝试 INSERT 时才触发等待,而等待发生前,间隙锁不会出现在 data_lock_waits 中。
- 现象:INSERT 一直 hang,但
data_lock_waits返回空结果 - 解法:先查
data_locks,过滤LOCK_MODE = 'GAP'或'NEXT_KEY',再看OBJECT_NAME和INDEX_NAME,结合业务逻辑判断哪些 INSERT 会落入该间隙 - 例如:表有主键
id值为 1,5,10,执行SELECT * FROM t WHERE id BETWEEN 2 AND 4 FOR UPDATE会加(1,5)间隙锁;后续INSERT INTO t(id) VALUES(3)就会被堵住,但堵之前data_lock_waits是空的 - 验证方式:在 INSERT 卡住时立刻查
data_lock_waits,这时才会出现记录,且LOCK_DATA显示类似3——说明它正试图插入这个值,而该值落在已被锁定的间隙内
真正难的不是查出锁,而是理解 LOCK_DATA 里的值或范围对应哪条业务逻辑,以及为什么那个事务会长时间持有它。间隙锁和临键锁的边界、索引选择是否走唯一键、隔离级别是否为 RR——这些细节一旦错一点,排查方向就全偏了。











