首页/数据/数据库速查/MDL 元数据锁等待排查
性能与调优57 条

MDL 元数据锁等待排查

DDL 等 MDL:长事务(哪怕只是 BEGIN 后没提交的读)会挡住 ALTER;DDL 又挡后面的 DML,形成雪崩队列。看 P_S.metadata_locks 定位。

数据库速查数据更新于 2026-10-13

MDL 是什么

元数据锁(Metadata Lock,简称 MDL)是 MySQL 5.5.3 引入的表级锁,用来保证语句执行期间表结构不被改动。任何查询和 DML 都会自动加 MDL 读锁,DDL 需要 MDL 写锁,两者互斥。因此一个没提交的事务就能把 ALTER TABLE 无限期堵住,而被堵的 DDL 又会把后面所有访问该表的语句排队堵住,形成雪崩式等待。

基本信息

适用版本MySQL 5.5.3 及以上,当前主线为 8.0
锁粒度表级(OBJECT_TYPE 为 TABLE),另有全局与库级
释放时机语句结束时(语句级)或事务提交/回滚时(事务级)
查看入口performance_schema.metadata_locks(5.7.3 起)、sys.schema_table_lock_waits
典型症状进程状态显示 Waiting for table metadata lock

典型使用场景

  • ALTER TABLE 执行后长时间不返回,需要定位到底被哪个连接挡住
  • 业务侧大量语句突然排队、连接数飙升,怀疑 DDL 与长事务互相阻塞
  • 上线前巡检:确认没有未提交的事务会挡住即将执行的 DDL
  • 排查应用关闭 autocommit 后遗留的空事务(只执行了 BEGIN 或一条 SELECT)
  • 备份、导出或加索引工具持锁,导致后续 DDL 全部失败

定位 MDL 现场

定位 MDL 现场
-- 1) 看谁持有、谁在等(把 appdb 换成目标库)
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_STATUS, LOCK_DURATION
FROM performance_schema.metadata_locks
WHERE OBJECT_SCHEMA='appdb' ORDER BY OBJECT_NAME, LOCK_STATUS;

-- 2) 推荐:sys 视图一步给出阻塞链
SELECT waiting_pid, waiting_query, blocking_pid, blocking_query
FROM sys.schema_table_lock_waits;

-- 3) 找最老的未提交事务(最常见的持锁源头)
SELECT trx_id, trx_state, trx_started, trx_mysql_thread_id
FROM information_schema.INNODB_TRX ORDER BY trx_started LIMIT 5;

阻塞链逐列读法

字段含义正常与异常
LOCK_TYPESHARED_READ / SHARED_WRITE 是普通语句加的读锁EXCLUSIVE 是 DDL 申请的写锁,出现且 PENDING 就是要等了
LOCK_STATUSGRANTED 已持有,PENDING 在排队PENDING 的那一行通常就是看起来卡住的 ALTER
LOCK_DURATIONSTATEMENT 语句级、TRANSACTION 事务级TRANSACTION 长时间不消失说明有事务没提交
OBJECT_NAME被锁的表名多张表同时 PENDING 说明有跨表 DDL 或批量操作
THREAD_ID与 threads / PROCESSLIST 关联的线程号拿到它才能确认源头并决定是否 KILL

确认后清理源头

确认后清理源头
-- 用 sys 视图给出的 blocking_pid 找到对应连接
SELECT id, user, host, db, command, time, state, info
FROM information_schema.PROCESSLIST WHERE id = 12345;
-- 确认这个事务可以被牺牲后再执行
KILL 12345;   -- 大事务的回滚可能比原语句更久
SHOW PROCESSLIST;   -- 复查等待队列是否已散开
MDL 等待很难靠超时兜底:lock_wait_timeout 默认 31536000 秒(约一年),实际等于无限等待。发现 DDL 被堵要主动定位处理,不要等它自己失败。
autocommit=1 时单条 SELECT 结束即释放 MDL,不会挡住 DDL;事故几乎都出在显式事务、关闭了自动提交的长连接,或事务里夹着慢查询。
优先处理 blocker 而不是 DDL:DDL 被中断后还要重来一遍,而等待期间业务已经受损;处理完源头再重新提交 DDL,并安排在低峰执行。

易错点

  • 把 DDL 当元凶直接 KILL:真正的源头是持锁的长事务,杀掉 DDL 只是让下一次重试再堵一次
  • 应用侧开着 autocommit=0 的长连接做只读查询:不提交就永不释放 MDL,DDL 会一直等下去
  • DDL 被堵后不断开新连接重试:等待队列叠加,把连接数推向 max_connections 上限
  • 只看业务库的进程列表:DDL 也可能被备份工具或跨库语句拖住,要连 P_S 与 sys 一起看

数据来源与参考

  • MySQL 8.0 Reference Manual: Metadata Locking

相关工具

​‌‌​​‌​​​‌‌​‌‌‌‌​‌‌​​​‌‌​‌‌‌​​‌‌​​‌​‌‌‌​​‌‌‌​​​‌​‌‌​‌​​‌​‌‌‌​​​‌​‌‌​‌​​‌​‌‌​​​‌‌​‌‌‌​‌​​​‌‌​‌‌‌‌​​‌​‌‌‌​​‌‌​​​‌‌​‌‌​‌‌‌‌​‌‌​‌‌​‌