要快速查出等待 metadatalock 的会话,需查询 performance_schema.events_waits_current 表筛选 wait/lock/metadata/sql/mdl 等待事件,再关联 threads 表获取连接详情;注意 performance_schema 必须启用且相关 consumers 开启,同时需排查长事务、information_schema 查询、空闲事务及 flush tables with read lock 等隐性锁源。

如何快速查出哪个会话在等 MetadataLock
MySQL 5.7+ 中,MetadataLock 等待不会直接出现在 SHOW PROCESSLIST 的 State 字段里,而是藏在 performance_schema 里。最直接的办法是查 events_waits_current 表:
SELECT THREAD_ID, EVENT_NAME, SOURCE, TIMER_WAIT FROM performance_schema.events_waits_current WHERE EVENT_NAME = 'wait/lock/metadata/sql/mdl' AND STATE = 'WAITING';
拿到 THREAD_ID 后,再关联 threads 表查对应连接信息:
SELECT t.PROCESSLIST_ID AS id, t.PROCESSLIST_USER AS user, t.PROCESSLIST_HOST AS host, t.PROCESSLIST_DB AS db, t.PROCESSLIST_INFO AS info FROM performance_schema.threads t WHERE t.THREAD_ID = ?; -- 替换为上一步查到的 THREAD_ID
- 注意:必须确保
performance_schema已启用(performance_schema=ON),且相关 consumers 已打开,尤其是events_waits_current和threads -
TIMER_WAIT是纳秒单位,值越大说明卡得越久,但为 0 不代表没卡——可能刚进入等待 - 如果查不到结果,先确认是否真有 DDL 正在执行(比如
ALTER TABLE、DROP INDEX),因为只有涉及元数据变更的操作才会持锁
为什么 SELECT 也会被 MetadataLock 卡住
很多人以为只有 DDL 才会触发元数据锁,其实只要事务中访问了某张表,该事务就持有了对该表的 MDL_SHARED_READ 锁;而 DDL 操作需要的是 MDL_EXCLUSIVE 锁——两者互斥。
典型场景:
- 一个长事务里执行了
SELECT * FROM orders,但没提交 - 此时另一个连接尝试
ALTER TABLE orders ADD COLUMN note VARCHAR(100) - DDL 会被阻塞,所有后续对
orders的查询(包括新SELECT)也都被堵住,因为它们要等 DDL 完成才能获取新的MDL_SHARED_READ
关键点在于:MDL 锁的生命周期和事务绑定,不是语句级的。即使只是普通查询,只要事务没结束,它就一直占着读锁。
哪些操作会持有或请求强级别 MDL 锁
不同操作申请的 MDL 类型不同,冲突强度也不同。容易引发阻塞的是以下几类(按常见程度排序):
-
ALTER TABLE、DROP TABLE、RENAME TABLE→ 需要MDL_EXCLUSIVE(最强,和其他所有类型都冲突) -
CREATE INDEX/DROP INDEX→ 同样需要MDL_EXCLUSIVE,尤其在大表上耗时久,阻塞窗口大 -
TRUNCATE TABLE→ 本质是DROP + CREATE,同样走MDL_EXCLUSIVE -
LOCK TABLES ... WRITE→ 会升级为MDL_SHARED_NO_WRITE,阻止 DDL,但允许 SELECT
对比之下:SELECT、INSERT、UPDATE 默认只申请 MDL_SHARED_READ 或 MDL_SHARED_WRITE,彼此不冲突,但全都会被 EXCLUSIVE 拦住。
排查时最容易忽略的三个点
很多 DBA 查半天发现不了源头,往往卡在这几个细节上:
-
information_schema表查询也会触发MDL——比如SELECT * FROM information_schema.TABLES WHERE TABLE_SCHEMA='xxx',虽然不显眼,但会持锁,且容易被当成“只读查询”而忽略 - 客户端自动重连或 ORM 连接池未正确关闭事务,导致“空闲事务”长期持有
MDL,SHOW PROCESSLIST看起来是Sleep,但锁还在 -
FLUSH TABLES WITH READ LOCK会全局加MDL_INTENTION_EXCLUSIVE,影响极大,且不会出现在常规等待视图中,需单独查global_read_lock状态或SHOW OPEN TABLES WHERE In_use > 0
真正卡住的时候,别只盯着活跃 SQL,得把“看似安静”的连接也翻一遍——特别是那些 Command=Sleep 但 Time 值很大的连接。











