mysql 8.0+需启用performance_schema及wait/lock相关instruments才能查data_locks和data_lock_waits;锁信息延迟可见,需结合innodb_status排查死锁,且注意不同版本lock_trx_id对应关系差异。

查哪些表能直接看到事务锁持有信息
MySQL 8.0+ 默认开启 performance_schema,但关键的锁视图(如 data_locks、data_lock_waits)默认不启用——不是“查不到”,而是“没开采集”。必须先确认配置项 performance_schema 是 ON,且锁相关 instruments 已启用。
实操建议:
- 运行
SELECT @@performance_schema;确认值为1 - 检查锁采集是否开启:
SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'wait/lock%';,确保data_locks和data_lock_waits对应的ENABLED列是YES - 若为
NO,需在启动时配置文件中添加:performance-schema-instrument='wait/lock/innodb%=ON',或运行时动态启用(仅限部分 instrument,锁类通常需重启)
怎么快速定位正在阻塞的事务和被锁的行
核心是组合查询 performance_schema.data_locks 和 performance_schema.data_lock_waits。前者记录当前所有锁(包括未阻塞的),后者只存“正在等待”的关系——但注意:data_lock_waits 不包含已释放的锁,也不包含未发生等待的独占锁。
常见错误现象:执行 SELECT * FROM performance_schema.data_lock_waits; 返回空,就以为“没锁”,其实可能只是没发生等待,或者锁在等待队列外(比如间隙锁未触发冲突)。
实操建议:
- 先看谁拿了锁:
SELECT ENGINE_TRANSACTION_ID, OBJECT_SCHEMA, OBJECT_NAME, INDEX_NAME, LOCK_TYPE, LOCK_MODE, LOCK_DATA FROM performance_schema.data_locks WHERE LOCK_TRX_ID IS NOT NULL; - 再查谁在等:
SELECT * FROM performance_schema.data_lock_waits;,重点关注BLOCKING_ENGINE_TRANSACTION_ID和REQUESTING_ENGINE_TRANSACTION_ID -
LOCK_DATA字段对行锁有意义(显示主键值或唯一索引值),对间隙锁显示supremum pseudo-record或具体范围;别指望它总能直接对应到业务数据
为什么查不到自己刚 UPDATE 的那条记录的锁信息
因为 InnoDB 行锁是“按需加锁”,不是语句一执行就立刻全量注册到 data_locks。例如:UPDATE t SET x=1 WHERE id=5; 在事务未提交前,确实持有 X 锁,但锁信息可能延迟几毫秒才刷入 data_locks;更常见的是,如果该语句走的是索引扫描(非唯一索引 + 范围条件),可能加了多个锁甚至间隙锁,但 data_locks 只返回其中一部分——这是采集粒度限制,不是漏了。
使用场景提醒:调试死锁时,不能只依赖 data_locks 快照,要配合 SHOW ENGINE INNODB STATUS\G 的 LATEST DETECTED DEADLOCK 段,那里有更实时、更完整的锁链快照。
性能影响:频繁查询 data_locks 本身不慢,但若事务长时间持有锁(尤其大事务),data_locks 表会持续增长,可能影响 performance_schema 内存占用;线上排查建议用完即查,不长期轮询。
data_locks.LOCK_TRX_ID 和 INFORMATION_SCHEMA.INNODB_TRX.TRX_ID 对不上怎么办
这是最常踩的坑:data_locks.LOCK_TRX_ID 是存储引擎层的事务 ID(64 位整数),而 INNODB_TRX.TRX_ID 是 InnoDB 内部用于事务管理的 ID,在 MySQL 8.0.26+ 中两者已统一;但在 8.0.25 及更早版本,data_locks.LOCK_TRX_ID 实际对应的是 INNODB_TRX.TRX_MYSQL_THREAD_ID(即连接线程 ID),不是 TRX_ID。
所以直接 JOIN 会关联失败。正确做法是:
- MySQL 8.0.26+:
SELECT * FROM performance_schema.data_locks d JOIN information_schema.INNODB_TRX t ON d.LOCK_TRX_ID = t.TRX_ID; - MySQL 8.0.25 及以前:
SELECT * FROM performance_schema.data_locks d JOIN information_schema.INNODB_TRX t ON d.LOCK_TRX_ID = t.TRX_MYSQL_THREAD_ID; - 更稳妥的通用方式:用
PROCESSLIST关联线程:SELECT d.*, p.USER, p.HOST, p.COMMAND, p.TIME FROM performance_schema.data_locks d JOIN performance_schema.threads th ON d.LOCK_TRX_ID = th.PROCESSLIST_ID JOIN information_schema.PROCESSLIST p ON th.PROCESSLIST_ID = p.ID;
真正麻烦的不是字段名,而是不同小版本间行为不一致——查锁前先确认 MySQL 小版本号,比背命令更重要。











