mysql 5.7+可通过performance_schema.metadata_locks与threads表关联查询阻塞ddl的元数据锁:先启用wait/lock/metadata/sql/mdl采集器,再执行join查询定位lock_status='pending'且lock_type='exclusive'的ddl等待线程及其blocking_pid持有者。

查 performance_schema 里的 metadata_locks 和 threads
MySQL 5.7+ 才有 performance_schema.metadata_locks 表,它记录了所有活跃的元数据锁(MDL)状态。但默认不开启,得先确认并启用:
-
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';—— 如果ENABLED是NO,执行UPDATE performance_schema.setup_instruments SET ENABLED = 'YES' WHERE NAME = 'wait/lock/metadata/sql/mdl'; - 重启连接或执行
FLUSH TABLES;让新配置生效(部分版本需重启实例) -
SELECT * FROM performance_schema.threads WHERE TYPE = 'FOREGROUND';可先看哪些是用户线程,方便后续关联
定位被阻塞的 DDL 线程:用 metadata_locks 关联 threads
DDL 操作(如 ALTER TABLE、DROP INDEX)在获取 MDL 时若被其他事务长期持有读锁(比如长事务没提交),就会卡在 PENDING 状态。关键查法:
SELECT
t.PROCESSLIST_ID AS blocked_pid,
t.PROCESSLIST_INFO AS blocked_sql,
t.PROCESSLIST_TIME AS blocked_time,
ml.OBJECT_SCHEMA,
ml.OBJECT_NAME,
ml.LOCK_TYPE,
ml.LOCK_DURATION,
ml.LOCK_STATUS,
blocking_t.PROCESSLIST_ID AS blocking_pid,
blocking_t.PROCESSLIST_INFO AS blocking_sql
FROM performance_schema.metadata_locks ml
JOIN performance_schema.threads t ON ml.OWNER_THREAD_ID = t.THREAD_ID
LEFT JOIN performance_schema.metadata_locks ml2 ON ml2.OBJECT_SCHEMA = ml.OBJECT_SCHEMA
AND ml2.OBJECT_NAME = ml.OBJECT_NAME
AND ml2.LOCK_STATUS = 'GRANTED'
AND ml2.LOCK_TYPE IN ('SHARED_READ', 'SHARED_WRITE', 'EXCLUSIVE')
LEFT JOIN performance_schema.threads blocking_t ON ml2.OWNER_THREAD_ID = blocking_t.THREAD_ID
WHERE ml.LOCK_STATUS = 'PENDING'
AND ml.LOCK_TYPE = 'EXCLUSIVE'
AND t.PROCESSLIST_INFO REGEXP '^(ALTER|CREATE|DROP|RENAME)';
注意:LOCK_TYPE = 'EXCLUSIVE' 是 DDL 的典型请求类型;LOCK_STATUS = 'PENDING' 表示它正等着锁释放。
为什么查不到?常见原因和绕过方式
即使启用了 instrument,也可能查不到阻塞线程,原因包括:
- 目标线程已超时退出(
lock_wait_timeout默认 31536000 秒,但实际 DDL 常被客户端中断) - 阻塞源是隐式锁:比如一个未提交事务执行了
SELECT ... FOR UPDATE后又访问了同表的其他行,可能让后续 DDL 卡住,但metadata_locks不会显示该事务“持有”MDL——它只持有了行锁,而 MDL 是在解析阶段就申请的 - MySQL 5.6 或更低版本不支持
metadata_locks,只能靠SHOW PROCESSLIST+INFORMATION_SCHEMA.INNODB_TRX组合推测:State显示Waiting for table metadata lock的线程就是被阻塞的 DDL,再找TIME > 60且COMMAND = 'Sleep'的长事务线程
快速止血:怎么临时解掉阻塞
生产环境不能等,优先 kill 掉阻塞源(不是被阻塞的 DDL):
- 从上一步查询结果中拿到
blocking_pid,执行KILL blocking_pid; - 如果不确定谁在 hold 锁,用
SELECT * FROM information_schema.INNODB_TRX ORDER BY TRX_STARTED LIMIT 10;找最老的未提交事务 - DDL 自身卡住超过预期时间,也可以
KILL blocked_pid;,但要注意某些 ALTER(尤其 ALGORITHM=COPY)中途 kill 可能留下临时表或损坏表结构
真正难的不是查,而是区分「谁在读表」和「谁在改表结构」——前者常藏在应用层无显式事务的查询里,后者却必须等前者彻底释放 MDL。这个等待链条一旦形成,光看 SQL 很难一眼识别。











