需先确认 performance_schema 已启用且锁相关采集器(wait/lock/、transaction/)和消费者均已开启,再验证 setup_instruments 中对应项 ENABLED 为 YES;否则 data_locks 等表将为空。

如何确认 performance_schema 已启用且锁监控未被关闭
MySQL 8.0 默认开启 performance_schema,但绝大多数锁相关事件采集器(如 wait/lock/、transaction/)默认是 DISABLED 的——这意味着你查 data_locks 或 events_waits_current 会返回空,不是没锁,而是根本没采集。
必须手动启用采集点和消费者:
UPDATE performance_schema.setup_instruments SET ENABLED = 'YES', TIMED = 'YES' WHERE NAME LIKE 'wait/lock/%';UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME IN ('global_instrumentation', 'thread_instrumentation', 'statements_digest');- 特别注意:
data_locks和data_lock_waits依赖transaction类采集器,别漏掉:UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME LIKE 'transaction%';
执行后务必验证:SELECT * FROM performance_schema.setup_instruments WHERE NAME LIKE 'wait/lock/%' AND ENABLED = 'YES'; —— 至少看到 10+ 行才基本可靠。
为什么查不到 data_lock_waits?三个常见原因
performance_schema.data_lock_waits 是 MySQL 8.0 中替代旧版 INNODB_LOCK_WAITS 的核心表,但它只记录“当前正在发生的阻塞”,一旦锁释放或事务提交,记录立刻消失。查不到 ≠ 没锁等待,很可能是时机问题或配置未生效。
排查顺序如下:
- 先确认是否启用了
transaction和wait/lock/两类采集器(上一节已说明); - 再检查是否在事务活跃期间查询:启动一个显式事务并加锁(如
SELECT ... FOR UPDATE),另起会话制造阻塞,**立刻**查data_lock_waits; - 如果仍为空,看
performance_schema.threads中对应线程的TYPE是否为FOREGROUND(后台线程如复制线程不参与锁采集); - 最后确认 MySQL 版本 ≥ 8.0.12 —— 早期 8.0 版本中该表字段名和行为有差异,例如
BLOCKING_ENGINE_LOCK_ID在 8.0.29 后才稳定。
metadata_locks 表能告诉你哪些“看不见”的阻塞
DML 锁(行锁)容易被关注,但 DDL 引发的元数据锁(METADATA LOCK)才是线上最常被忽略的卡点:ALTER TABLE、DROP INDEX、甚至 SELECT 都可能因持有 S 或 X 元数据锁而阻塞后续语句。
用这个查询定位谁在等表级锁:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING';
再关联 performance_schema.threads 和 sys.session 找到具体连接和 SQL:
-
OWNER_THREAD_ID可直接 jointhreads.THREAD_ID获取PROCESSLIST_ID; - 然后用
SHOW FULL PROCESSLIST或查sys.session看current_statement; - 注意:
LOCK_DURATION = 'TRANSACTION'表示该锁会持续到事务结束,哪怕只是个SELECT也没释放。
events_waits_history_long 是唯一能“回溯”锁等待的表
events_waits_current 只存每个线程最新一条等待事件,events_waits_history 每线程仅保留 10 条——对排查偶发长等待毫无帮助。真正有用的只有 events_waits_history_long,它按全局 FIFO 存储最多 10000 条历史等待事件。
但它的陷阱在于:
- 默认不开启写入,需提前执行:
UPDATE performance_schema.setup_consumers SET ENABLED = 'YES' WHERE NAME = 'events_waits_history_long'; - 它不区分锁类型,得靠
EVENT_NAME过滤,典型值包括:wait/lock/metadata/sql/mdl(元数据锁)、wait/synch/mutex/innodb/buf_pool_mutex(InnoDB 内部锁); - 查完记得关,否则内存占用增长明显:
TRUNCATE TABLE performance_schema.events_waits_history_long;是安全清空方式,不是DROP。
真正难的不是查到锁,而是把 waiting_trx_id → thread_id → processlist_id → SQL text 这条链路串全,中间任何一环缺失(比如线程已退出、history 被覆盖、采集器未开),证据就断了。










