DDL卡在“Waiting for table metadata lock”是因为MDL排他锁等待:长事务或未提交SELECT持有MDL读锁,而ALTER TABLE需MDL_EXCLUSIVE锁,必须等所有读锁释放才能执行。

DDL执行时卡在“Waiting for table metadata lock”是怎么回事
这表示当前 ALTER TABLE 被阻塞在元数据锁(MDL)获取阶段,不是表数据锁,而是对表结构定义本身的访问控制。InnoDB 不会直接锁表数据,但 MySQL Server 层必须确保 DDL 期间没人正在读/写这张表的结构——比如另一个事务正拿着 SELECT 或 UPDATE 持有 MDL 读锁,而 DDL 需要排他 MDL 锁(MDL_EXCLUSIVE),就会等在那里。
常见诱因包括:
- 长事务未提交:哪怕只是
SELECT * FROM t WHERE id = 1开了事务没COMMIT,也会持续持有 MDL 读锁 - 从库 SQL 线程卡住:SHOW SLAVE STATUS 中
SQL_Remaining_Delay非零或Seconds_Behind_Master持续上涨,说明复制线程本身被阻塞,后续 DDL 无法推进 - 显式
LOCK TABLES ... WRITE未释放
为什么加个索引也会触发 MDL 排他锁
因为索引是表结构的一部分,任何 CREATE INDEX、ALTER TABLE ... ADD INDEX 都属于 DDL,MySQL 必须保证在构建索引过程中,没人能删列、改列类型、甚至 DROP 这张表。所以它会申请 MDL_EXCLUSIVE 锁——注意,这不是为了拷数据,而是为了“冻结结构定义”。
即使你写了 ALGORITHM=INPLACE, LOCK=NONE,这个组合只影响数据行的读写是否被阻塞,不影响 MDL 锁本身的申请流程。也就是说:LOCK=NONE 是说“允许其他连接继续 INSERT/UPDATE/DELETE”,但 MDL_EXCLUSIVE 仍需等待所有现有 MDL 读锁释放。
典型错误现象:
-
SHOW PROCESSLIST显示状态为Waiting for table metadata lock -
SELECT * FROM information_schema.innodb_trx查到活跃事务里有长时间运行的SELECT - 执行
KILL后,新 DDL 依然卡住——说明还有别的会话在持锁
如何快速定位谁在占用 MDL 锁
别只盯着 information_schema.innodb_trx,MDL 锁持有者可能根本没开启事务。真正要看的是 performance_schema.metadata_locks(MySQL 5.7+ 默认开启):
SELECT OBJECT_SCHEMA, OBJECT_NAME, LOCK_TYPE, LOCK_DURATION, LOCK_STATUS, OWNER_THREAD_ID FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'your_db' AND OBJECT_NAME = 'your_table';
配合 performance_schema.threads 可查出具体是哪个客户端线程在持锁。关键字段:
-
LOCK_TYPE = 'EXCLUSIVE'或'SHARED_HIGH_PRIO'表示写入/结构变更类操作正在执行中 -
LOCK_STATUS = 'PENDING'表示当前语句在等锁,不是持锁方 -
OWNER_THREAD_ID对应threads.THREAD_ID,可反查PROCESSLIST_ID
如果 performance_schema 关闭,退而求其次用:
SELECT ID, USER, HOST, DB, COMMAND, TIME, STATE, INFO FROM information_schema.PROCESSLIST WHERE COMMAND != 'Sleep' AND (INFO LIKE '%your_table%' OR DB = 'your_db');
ALTER TABLE 执行一半被 kill 会怎样
MySQL 的 DDL 大部分是原子性的,但“执行一半”取决于卡在哪一阶段:
- 还在 MDL 等待阶段被 KILL:什么都不会发生,语句直接退出,无副作用
- 已进入 INPLACE 阶段(如建索引中)被 KILL:InnoDB 会回滚该操作,已写入的索引页会被清理,表回到原状;但可能留下临时日志或碎片
- 已降级为 COPY 算法(
copy to tmp table)被 KILL:MySQL 会尝试删除临时表,但若磁盘 I/O 卡住,可能残留#sql-*临时文件,需手动rm并重启 mysqld 清理
最危险的是在从库上 KILL 正在执行的 DDL:可能中断 relay log 应用位置,导致 Slave_SQL_Running: No 且 Exec_Master_Log_Pos 停滞,后续需人工校验 GTID 或 binlog 位点再跳过。
MDL 锁本身不写磁盘,也不占 buffer pool,但它像一把看不见的门禁——只要有人没松手,所有人就只能站在门外等。真正难排查的,永远是那个忘了 COMMIT 的连接,或者那个开着事务查了十分钟慢日志却没关的运维窗口。











