mysql 5.7 online ddl仍触发mdl锁,因其本质是“元数据级在线”,非“无锁”:所有ddl必须获取mdl_exclusive锁以安全修改表定义,初始化和提交阶段均需持有,遇长事务即卡在“waiting for table metadata lock”。

MySQL 5.7 Online DDL 为什么仍会触发 MDL 锁
因为 Online DDL 不等于“无锁 DDL”——它只是把锁的粒度从“全表阻塞”降为“元数据锁(MDL)等待”,而 MDL 锁本身在关键阶段无法绕过。
在 MySQL 5.7 中,ALGORITHM=INPLACE 能避免物理重建表,但所有 DDL 操作都必须获取写模式的 MDL 锁(MDL_EXCLUSIVE),才能安全修改表定义。这个锁会在操作开始前申请,在最后原子提交阶段释放。期间若有长事务正持有该表的读锁(比如一个未提交的 SELECT),DDL 就会卡在“Waiting for table metadata lock”状态。
- MDL 锁是语句级的,不是事务级的:哪怕你只执行一条
ALTER TABLE ... ADD COLUMN,也会阻塞后续所有对该表的 DML,直到它拿到锁 -
LOCK=NONE在 5.7 中支持极有限:仅对加索引、加列(末尾)、改列默认值等少数操作有效;像MODIFY COLUMN或调整VARCHAR长度,基本都会退化成LOCK=SHARED或更严的锁 - 即使 DDL 内部用的是 INPLACE 算法,只要它需要变更列定义、索引结构或主键,就必须串行化访问表元数据——这就是 MDL 的存在意义
哪些 Online DDL 操作在 5.7 中必然卡住 MDL
不是所有 ALTER TABLE 都能“悄悄进行”。以下操作在 MySQL 5.7 中几乎必定引发明显 MDL 等待,尤其当表上有活跃事务时:
-
CHANGE COLUMN或MODIFY COLUMN:涉及列类型/长度变更,需校验兼容性并重写字段元数据,强制要求MDL_EXCLUSIVE -
VARCHAR长度收缩(如VARCHAR(200) → VARCHAR(100)):不支持 INPLACE,回退到 COPY 算法,全程持有排他 MDL -
VARCHAR跨字节边界扩容(如VARCHAR(255) → VARCHAR(256)):长度字节数从 1 字节升为 2 字节,必须重建,触发 COPY + 全程 MDL 排他 - 添加全文索引(
FULLTEXT INDEX):5.7 不支持 INPLACE 创建全文索引,只能 COPY,且过程不可中断
这些操作一旦遇到未提交的 SELECT 或慢查询,就会在 SHOW PROCESSLIST 中显示为 Waiting for table metadata lock,而不是报错退出。
如何快速判断当前 DDL 是否会卡 MDL
别靠猜,用明确参数组合提前验证:
- 先加
ALGORITHM=INPLACE, LOCK=NONE执行,如果报错ERROR 0A000: ALGORITHM=INPLACE is not supported或LOCK=NONE is not supported,说明该操作无法规避 MDL 等待 - 检查
information_schema.INNODB_TRX和performance_schema.metadata_locks(5.7.3+)确认是否有事务正在占用目标表 - 运行
SELECT * FROM sys.innodb_lock_waits;(需启用sysschema)看是否已有锁等待链 - 对大表,宁可先在从库上试跑:用
STOP SLAVE; ALTER TABLE ...; START SLAVE;观察是否卡住,再推主库
为什么不能简单关掉 MDL 锁
MDL 锁不是性能开关,而是并发安全的基石。禁用它会导致:ALTER TABLE 和 SELECT 同时读写同一份表定义内存结构,可能让查询解析出错误列偏移、跳过新加字段、甚至 crash mysqld。
真正要解决的不是“去掉 MDL”,而是缩短它被持有的时间:
- 避开业务高峰执行 DDL,减少与长事务撞车概率
- 提前
KILL掉已知的慢查询或空闲连接(查SHOW PROCESSLIST WHERE Command != 'Sleep') - 设置
lock_wait_timeout=10(单位秒),让 DDL 在等锁超时后自动失败,而非无限挂起 - 对无法接受任何等待的场景,直接切到
pt-online-schema-change——它不依赖 MDL,而是靠触发器和重放绕过元数据变更阶段
最易被忽略的一点:MySQL 5.7 的 ALGORITHM=INPLACE 是“尽力而为”,不是承诺。它只保证不 COPY 数据,但不保证不等锁。把“在线”理解成“不阻塞 DML”是错的——准确说是“DML 可以继续执行,但得排队等 MDL”。











