定位一条 UPDATE 长时间不返回的原因时,光看 InnoDB 状态输出通常会错过元数据锁这一类隐性等待。Performance Schema 里的锁监控事件能够把等待线程、持锁线程、锁类型和涉及的库表都记录下来,但前提是相关 instruments 和 consumers 已经被正确打开。下面直接进入配置和查询链路。

锁监控事件涉及的核心配置表
在 Performance Schema 中,setup_instruments 表控制每个采集点是否启用以及是否计时。锁相关的 instrument 主要有 wait/lock/metadata/sql/mdl,它负责收集元数据锁等待。行级锁和表级锁信息则更多依赖 InnoDB 内部的锁表,但 Performance Schema 本身必须开启,data_locks 和 data_lock_waits 才会有内容。还有一个 wait/lock/table/sql/handler,如果还需要观察 LOCK TABLES 这类表锁行为,可以一并打开。
另一张配置表是 setup_consumers,它决定采集到的事件被写到哪些缓冲表里。对于锁等待追踪,至少要确认 global_instrumentation、thread_instrumentation 以及 events_waits_current 处于启用状态。如果希望保留历史等待记录,可以打开 events_waits_history 和 events_waits_history_long,这样即便等待已经结束,也能从历史表中回查。
需要特别区分的是,data_locks 显示的是当前已经被获取或正在等待的行锁、间隙锁、插入意向锁等,data_lock_waits 则专门表示发生等待的锁关系。metadata_locks 表依赖 wait/lock/metadata/sql/mdl 这个 instrument。如果你没有打开它,表里可能长时间为空,即使当前确实存在元数据锁阻塞。
逐步开启锁等待追踪
第一步先确认 Performance Schema 自身的开关。执行 SHOW VARIABLES LIKE 'performance_schema';,返回值为 ON 表示已启用。如果是 OFF,需要修改 MySQL 配置文件后重启实例。MySQL 8.0 通常默认开启,但某些云数据库或精简部署可能关闭。
第二步打开 MDL 锁采集。可以用下面的 SQL 批量启用所有以 wait/lock/metadata/sql/mdl 开头的 instruments,同时开启计时,方便后面观察等待时长。
-- 启用元数据锁相关采集点 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/lock/metadata/sql/mdl%'; -- 如果需要观察表锁,也可以打开表锁采集点 UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/lock/table/sql/handler%';
第三步打开等待事件消费者。如果只打开 instrument 而不打开 consumer,事件无法被存储和查询。
-- 确认全局采集开关 UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'global_instrumentation'; -- 打开当前等待、历史等待和长历史等待 UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME LIKE 'events_waits%';
配置完成后,可以查询 setup_instruments 和 setup_consumers 验证是否已经生效。需要注意的是,这些修改在实例重启后不一定保留,如果希望永久生效,需要把同样的配置写入启动初始化 SQL 或由运维平台统一执行。
查询锁等待链路的实用SQL
对于 InnoDB 行锁等待,最直接的定位方式是联合 performance_schema.data_lock_waits 和 information_schema.innodb_trx。前者记录等待方和阻塞方的事务 ID,后者提供事务对应的线程 ID、SQL 文本和状态。下面这条 SQL 可以找出谁在等谁,以及双方正在执行什么语句。
SELECT r.trx_id AS waiting_trx_id, r.trx_mysql_thread_id AS waiting_thread, r.trx_query AS waiting_query, b.trx_id AS blocking_trx_id, b.trx_mysql_thread_id AS blocking_thread, b.trx_query AS blocking_query, w.blocking_lock_id AS lock_id FROM performance_schema.data_lock_waits w JOIN information_schema.innodb_trx b ON b.trx_id = w.blocking_trx_id JOIN information_schema.innodb_trx r ON r.trx_id = w.requesting_trx_id;
如果想进一步确认锁的模式和锁定对象,可以把 data_locks 表关联进来。重点看 lock_type、lock_mode 和 lock_status,例如 RECORD 行锁、X 排他锁、WAITING 等待状态。这样就能知道等待发生在一个主键记录、二级索引上,还是间隙锁上。
SELECT l.engine_lock_id, l.engine_transaction_id, l.object_name, l.index_name, l.lock_type, l.lock_mode, l.lock_status, l.lock_data FROM performance_schema.data_locks l JOIN performance_schema.data_lock_waits w ON l.engine_lock_id = w.blocking_lock_id;
对于元数据锁等待,直接查询 performance_schema.metadata_locks 往往更有效。它能显示 SHARED_READ、EXCLUSIVE 等 MDL 类型,以及锁对象所在库表。把 metadata_locks 和 performance_schema.threads 关联后,可以拿到对应的连接 ID 和 SQL 信息。下面这条 SQL 适合用来排查 DDL 或 DML 被 MDL 阻塞的场景。
SELECT m.object_schema, m.object_name, m.lock_type, m.lock_status, m.owner_thread_id, t.processlist_id, t.processlist_command, t.processlist_state, t.processlist_info FROM performance_schema.metadata_locks m LEFT JOIN performance_schema.threads t ON m.owner_thread_id = t.thread_id WHERE m.object_schema IS NOT NULL ORDER BY m.object_schema, m.object_name;
如果不熟悉底层表结构,也可以使用 sys.schema_table_lock_waits 视图,它内部整合了元数据锁信息,输出更加直观。但视图依赖底层 instruments 已开启,否则查询结果可能不完整。
生产环境开启监控的注意事项
锁监控虽然能帮助定位阻塞,但每个 MDL 获取和释放都会经过探针,开启后可能对高并发短查询带来可感知的 CPU 开销。建议不要在业务高峰期全量打开所有锁相关 instruments,可以先用一个窗口期采集,复现问题后立即关闭。常见的折中方法是只开启 wait/lock/metadata/sql/mdl,而不打开表锁或者更细粒度的同步对象监控。
消费者表也有内存上限,events_waits_history_long 默认保存 10000 行,超出后会覆盖旧数据。如果锁等待发生得很快,历史表可能已经失去现场,因此最好结合 data_lock_waits 和 data_locks 这类实时表进行观察。对于已经消失的等待,仍可以通过 events_waits_history 按线程回查,但需要知道线程 ID 才能缩小范围。
最后还需要区分信息粒度。Performance Schema 给出的锁等待链路偏向事务和线程级别,而 SHOW ENGINE INNODB STATUS 的 LATEST DETECTED DEADLOCK 段落更适合查看已经发生的死锁。两者配合使用,再结合 information_schema.innodb_trx 中的 trx_started 时间,可以快速判断哪个事务长时间未提交,从而决定是否终止阻塞源。
Performance Schema锁等待追踪MySQL锁监控修改时间:2026-09-27 14:22:01