可通过performance_schema.metadata_locks表精准定位MDL阻塞会话,执行指定JOIN查询可同时获取等待方与持有方信息,需确保performance_schema及wait/lock/metadata/sql/mdl仪器已启用。

如何快速定位正在阻塞的Metadata Lock会话
Metadata Lock(MDL)等待不会出现在 SHOW PROCESSLIST 的 State 列里显示为 “Waiting for table metadata lock”,但实际已被锁住的线程可能卡在 Waiting for table metadata lock 或更隐蔽的状态(比如 Opening tables、After opening tables)。真正有效的排查入口是 performance_schema。
执行以下查询可直接看到谁在等、谁在持锁:
SELECT r.OBJECT_SCHEMA, r.OBJECT_NAME, r.LOCK_TYPE, r.LOCK_DURATION, r.LOCK_STATUS, r.SOURCE, r.OWNER_THREAD_ID, r.OWNER_EVENT_ID, b.PROCESSLIST_ID AS BLOCKING_PID, b.PROCESSLIST_USER AS BLOCKING_USER, b.PROCESSLIST_HOST AS BLOCKING_HOST, b.PROCESSLIST_INFO AS BLOCKING_QUERY FROM performance_schema.metadata_locks r JOIN performance_schema.threads t ON r.OWNER_THREAD_ID = t.THREAD_ID JOIN performance_schema.metadata_locks b ON r.OBJECT_SCHEMA = b.OBJECT_SCHEMA AND r.OBJECT_NAME = b.OBJECT_NAME AND b.LOCK_STATUS = 'GRANTED' AND r.LOCK_STATUS = 'PENDING' AND r.LOCK_TYPE = b.LOCK_TYPE AND r.LOCK_DURATION = b.LOCK_DURATION JOIN performance_schema.threads bt ON b.OWNER_THREAD_ID = bt.THREAD_ID JOIN performance_schema.processlist bpl ON bt.THREAD_ID = bpl.THREAD_ID;
- 必须确保
performance_schema已启用,且metadata_locks表已开启(5.7+ 默认开启,但部分旧部署可能被禁用) - 若查不到结果,先确认:
SELECT * FROM performance_schema.setup_instruments WHERE NAME = 'wait/lock/metadata/sql/mdl';返回ENABLED = YES - 注意:该查询本身也会申请 MDL,若系统已严重阻塞,可能自己也被卡住 —— 建议在低峰期或连接到从库(如果从库未关闭
performance_schema)运行
哪些操作容易触发长时间MDL持有
MDL 不是事务锁,它按语句生命周期持有,但某些操作会**隐式延长持有时间**,尤其在未提交事务或长事务中。
-
ALTER TABLE在 5.6+ 仍需在开始和结束阶段获取SCH(schema)级 MDL,期间阻塞所有 DML;即使使用ALGORITHM=INPLACE,也仍需短时排他 MDL - 一个未提交的
SELECT(哪怕只是SELECT ... FROM t LIMIT 1)在事务中,会持续持有该表的SHARED_READMDL,导致后续ALTER或DROP卡住 -
FLUSH TABLES WITH READ LOCK会持有全局EXCLUSIVEMDL,影响所有表,且不随事务结束释放 - 显式
LOCK TABLES t READ/WRITE同样延长 MDL 持有,且与事务隔离 —— 即使事务已提交,只要没执行UNLOCK TABLES,锁就还在
如何安全终止持锁会话避免雪崩
找到阻塞源头后,不能直接 KILL 所有疑似会话。MySQL 的 MDL 等待队列是 FIFO,粗暴 KILL 等待者可能让下一个等待者立刻顶上,而真正持锁的会话若正在执行大事务或慢查询,KILL 它反而触发回滚,加重负载。
- 优先
KILL持锁方(即LOCK_STATUS = 'GRANTED'那行对应的PROCESSLIST_ID),尤其是状态为Query且INFO显示长时间运行的ALTER或大事务 - 若持锁会话状态是
Sleep且COMMAND = 'Sleep',大概率是应用端忘了提交事务 —— 此时KILL是最有效解法 - 避免
KILL QUERY:对 MDL 场景无效,因为锁不在语句粒度释放,而在会话/事务边界释放 - 生产环境建议加
--connect-timeout=2和--max-allowed-packet=64M再连上去执行KILL,防止客户端自身被卡住
长期规避MDL争用的关键配置与习惯
治标靠 kill,治本靠约束。MDL 本身不可禁用,但可通过配置和开发规范大幅降低风险。
- 设置
lock_wait_timeout(默认 31536000 秒)为合理值,如SET SESSION lock_wait_timeout = 60;,让等待自动失败而非无限挂起 - 禁止应用在事务中执行无关
SELECT;ORM 框架开启autocommit=True,避免隐式事务长期持锁 - DDL 变更务必在低峰期进行,并搭配
pt-online-schema-change或gh-ost,它们通过影子表+触发器绕过大部分 MDL 排他等待 - 监控项必须包含:
select count(*) from performance_schema.metadata_locks where LOCK_STATUS = 'PENDING';,超过阈值(如 > 3)立即告警
MDL 锁没有超时重试机制,一旦形成等待链,它就会静默堆积 —— 最容易被忽略的是那些“看起来没在干活”的 Sleep 会话,它们可能正握着一把没人注意到的锁。


















