通过查询performance_schema.metadata_locks可快速定位阻塞DDL的会话:执行SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, OWNER_THREAD_ID FROM metadata_locks WHERE LOCK_STATUS = 'PENDING' OR LOCK_TYPE = 'EXCLUSIVE',再关联threads表获取连接信息,精准识别持有排他锁或正在等待锁的线程。

如何快速定位阻塞 DDL 的会话
MySQL 5.7+ 默认启用 performance_schema,其中 metadata_locks 表是直接观测元数据锁(MDL)持有与等待关系的核心入口。它比拼接 INNODB_TRX + threads 更准、更实时,且无需依赖 sys 库(某些生产环境可能禁用或未初始化)。
执行以下查询即可看到当前所有 MDL 持有者和等待者:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID, OWNER_EVENT_ID FROM performance_schema.metadata_locks WHERE LOCK_STATUS = 'PENDING' OR LOCK_TYPE = 'EXCLUSIVE';
关键点:
-
LOCK_TYPE = 'EXCLUSIVE'表示该线程正持有排他 MDL(比如一个未提交的SELECT ... FOR UPDATE或长事务中的普通SELECT) -
LOCK_STATUS = 'PENDING'表示该线程正在等待获取 MDL(通常是被阻塞的ALTER TABLE) - 通过
OWNER_THREAD_ID关联performance_schema.threads可查到对应连接的PROCESSLIST_ID和PROCESSLIST_INFO
为什么 SHOW PROCESSLIST 看不到阻塞源头
因为阻塞 DDL 的会话常常处于 Sleep 状态,而非活跃执行中。例如一个应用开启事务后执行了 SELECT * FROM t,然后卡在业务逻辑里没提交——SHOW PROCESSLIST 显示它是 Sleep,Info 字段为空,根本看不出它还持有着表 t 的 MDL。
这种“静默持有”正是最易被忽略的阻塞源。此时 performance_schema.metadata_locks 仍能准确标记其 LOCK_TYPE 和 OBJECT_NAME,而 threads 表里的 PROCESSLIST_INFO 字段可能为 NULL,需结合 THREAD_OS_ID 查操作系统级堆栈(如需深度排查)。
如何关联出阻塞者的实际 SQL
仅靠 metadata_locks 得到的是线程 ID,不是 SQL。必须连查 performance_schema.events_statements_current 才能看到最近执行语句:
SELECT t.PROCESSLIST_ID, t.PROCESSLIST_INFO, es.SQL_TEXT FROM performance_schema.threads t JOIN performance_schema.events_statements_current es ON t.THREAD_ID = es.THREAD_ID WHERE t.THREAD_ID IN ( SELECT OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE LOCK_TYPE = 'EXCLUSIVE' AND OBJECT_NAME = 'your_table_name' );
注意:
- 若
SQL_TEXT为NULL,说明该线程当前没有活跃语句(典型如事务已空闲但未提交) - 若
PROCESSLIST_INFO是NULL但SQL_TEXT有内容,说明语句刚执行完、尚未被清理,仍有效 - MySQL 8.0 默认开启事件采集;5.7 需确认
setup_consumers中events_statements_current为YES
KILL 前必须确认的三件事
盲目 KILL 会破坏事务一致性,尤其当阻塞者是报表导出、批量更新等合法长事务时。
执行 KILL <code>pid 前务必确认:
- 该连接是否属于核心业务应用(查
threads.PROCESSLIST_USER和HOST) - 其事务是否修改了数据(查
INNODB_TRX.TRX_ROWS_MODIFIED,若为 0 且长时间无动作,大概率可安全终止) - 是否有上游服务正在重试或依赖该连接(如连接池配置了 validateQuery,KILL 后可能引发雪崩)
真正难处理的不是锁本身,而是无法判断“这个 Sleep 连接到底还在不在干活”。metadata_locks 能告诉你谁拿了锁,但不能告诉你它拿锁的意图——这需要结合业务上下文交叉验证。


















