← 返回题目列表

MySQL 元数据锁 MDL 是什么?为什么 ALTER TABLE 会被阻塞?

中等 第 22 / 28 题 更新于 2026/07/30
MySQLMDL元数据锁DDL

简化版

MDL 是 MySQL 用来保护表结构元数据一致性的锁。普通查询会持有元数据读锁,DDL 如 ALTER TABLE 需要元数据写锁。如果一个长事务查询了某张表但迟迟不提交,DDL 可能拿不到写锁而等待;同时后续新查询又可能排在 DDL 后面,造成大量请求阻塞。排查时要关注长事务、DDL 等待和 processlist。

详细版

MDL 的目标是防止查询过程中表结构被改掉。比如一个事务正在读取表,另一个会话不能随意删除或修改表结构,否则执行语义会混乱。

问题常出在长事务。即使只是执行过一个 SELECT,只要事务未提交,相关表的 MDL 读锁可能持续存在。此时 ALTER TABLE 想拿写锁会等待,而等待中的写锁又会阻塞后续读锁,最终看起来像整张表都卡住。

面试中要讲清 MDL 和 InnoDB 行锁不是一类锁:MDL 保护表结构,行锁保护数据行。

完整版教学

一、MDL 保护什么

MDL 全称 metadata lock。

它保护表结构、字段、索引等元数据。

查询执行时需要表结构稳定。

DDL 修改结构时需要独占元数据。

所以读写元数据之间必须协调。

二、典型阻塞链路

会话 A 开启事务并查询表。

会话 A 长时间不提交。

会话 B 执行 ALTER TABLE,等待 MDL 写锁。

会话 C 再查询同一张表,也被排队影响。

业务上表现为大量 SQL 卡住。

三、示例流程

-- session A
BEGIN;
SELECT * FROM user_account WHERE id = 1;
-- 长时间不 COMMIT

-- session B
ALTER TABLE user_account ADD COLUMN remark VARCHAR(64);

如果 A 的事务不结束,B 可能一直等待。

线上 DDL 前要特别检查长事务。

四、MDL 和行锁对比

锁类型保护对象常见场景
MDL表结构元数据SELECT、DDL、DROP TABLE
行锁数据行UPDATE、DELETE、SELECT FOR UPDATE
间隙锁索引范围间隙可重复读下范围更新
表锁整张表数据访问特定引擎或显式锁表

不要把 MDL 问题误判成普通行锁死锁。

五、为什么 DDL 会放大影响

DDL 等待 MDL 写锁时,会进入队列。

后续读请求可能排在等待中的 DDL 后面。

于是一个长事务加一个 DDL,就可能造成大量普通查询阻塞。

这也是线上变更窗口和在线 DDL 工具很重要的原因。

MDL 事故常常不是 ALTER 本身慢,而是 ALTER 等锁期间把后续请求也拖住了。

六、排查和处理

查看 processlist 中的 Waiting for table metadata lock。

查找长事务和未提交会话。

确认是否有 DDL 正在等待。

必要时终止阻塞源会话。

上线前用工具或脚本检查长事务。

七、误区和追问

  • 误区:SELECT 不会加锁。 普通 SELECT 不加行锁,但会持有元数据读锁。
  • 误区:MDL 是 InnoDB 行锁。 MDL 属于 MySQL server 层元数据锁。
  • 误区:小表 ALTER 一定安全。 如果有长事务持有 MDL,哪怕小表也可能阻塞。
  • 追问:为什么后续 SELECT 也被卡住? 等待中的 DDL 写锁可能阻塞后续新的元数据读锁。
  • 追问:如何预防 MDL 事故? DDL 前检查长事务,控制变更窗口,使用在线 DDL 工具并设置超时。
  • 追问:杀哪个会话? 通常要找最早持有 MDL 的长事务或阻塞源,而不是盲目杀所有查询。

八、面试收束

回答时先定义 MDL。

再讲长事务阻塞 DDL、DDL 队列阻塞后续查询的链路。

最后补充 processlist 排查和上线预防。