ALTER TABLE必须等待MDL写锁,因其需确保表结构变更时无其他线程读写元数据,防止“字段消失”或“类型突变”等不一致;该锁为强制机制,卡在Waiting for table metadata lock主因是前置未释放的MDL读锁,如未提交的SELECT、ORM隐式事务或连接池游标未关闭。

ALTER TABLE 为什么必须等 MDL 写锁
因为 MySQL 要保证表结构变更期间,没有任何其他线程正在读或写这张表的元数据——否则会出现“查到一半字段没了”或“插入时字段类型已变”这类数据不一致问题。MDL 写锁不是可选项,是强制保护机制,只要执行 ALTER TABLE,就必须拿到它,否则操作直接卡住,不会报错也不会跳过。
常见卡在 Waiting for table metadata lock 的真实原因
不是 ALTER TABLE 本身慢,而是它被前面某个没释放的 MDL 读锁拦住了。这些读锁来源往往很隐蔽:
-
SELECT * FROM t后忘了COMMIT或ROLLBACK,哪怕只是个简单查询,事务开着就一直持MDL_SHARED_READ - ORM 框架(如 Django、MyBatis)隐式开启事务后未关闭,比如“读已提交”预热查询后没显式结束事务
- 连接池配置不当,应用取完连接没 close,游标还挂着,
performance_schema.threads里能看到活跃会话但INNODB_TRX查不到 - 存储过程或事件调度器里有未提交的语句,长期静默持有锁
ALGORITHM=INPLACE 也救不了你的情况
很多人以为加了 ALGORITHM=INPLACE 就能避免锁表,其实只对“重建阶段”有效;DDL 开始前和提交时仍需短暂升级到 MDL_EXCLUSIVE 锁,这时照样会被未释放的读锁阻塞。
-
ADD COLUMN在 MySQL 5.7 中仍需拷贝聚集索引,无法跳过锁等待 -
MODIFY COLUMN或DROP COLUMN基本都走 copy 算法,全程持MDL_EXCLUSIVE - MySQL 8.0.12+ 的
ALGORITHM=INSTANT确实只改元数据,但要求表无全文索引、无虚拟列、row_format必须是DYNAMIC或COMPRESSED
怎么快速定位谁在死死攥着 MDL 读锁
别只盯着 information_schema.INNODB_TRX,它只显示事务级活跃操作;MDL 锁可能来自自动提交语句或空闲连接。真正有效的组合命令是:
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, THREAD_ID FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
再关联 performance_schema.threads 查出对应线程的 SQL 和状态,重点看 PROCESSLIST_INFO 是否为空、PROCESSLIST_STATE 是否为 Waiting for table metadata lock 或 Sleep。
最容易被忽略的一点:MDL 锁生命周期不依赖事务提交,而依赖连接是否关闭、语句是否执行完毕。一个空闲连接只要之前执行过任何涉及该表的语句,就可能还在持锁。


















