根本原因是alter需两次申请mdl_exclusive锁,被未提交的select等持有的mdl_shared_read锁阻塞;执行前须查长事务、processlist及metadata_locks持锁情况,并慎用algorithm=inplace和lock=none。

为什么ALTER会卡在Waiting for table metadata lock
根本原因不是DDL本身慢,而是它在开头和结尾两次申请MDL_EXCLUSIVE锁时被阻塞。哪怕只是个没提交的SELECT * FROM orders,也会让后续所有ALTER TABLE停在“等待元数据锁”状态——不报错、不超时、只排队,直到长事务结束或连接池耗尽。
执行前必须查的三个关键点
别跳过检查,否则等于把锁表风险直接扔给线上:
-
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60:有运行超1分钟的事务,就别跑DDL -
SHOW PROCESSLIST:找状态为Waiting for table metadata lock的线程,顺藤摸瓜定位源头SQL -
SELECT * FROM performance_schema.metadata_locks WHERE OBJECT_SCHEMA = 'db_name' AND OBJECT_NAME = 'table_name':确认当前谁持有MDL_SHARED或MDL_EXCLUSIVE
ALGORITHM=INPLACE, LOCK=NONE不是万能解药
这两个参数只控制执行阶段是否重建数据页,不影响开头/结尾抢MDL_EXCLUSIVE的逻辑。而且LOCK=NONE在5.7中支持极窄:
- 仅对末尾加列、加索引、改默认值有效
-
MODIFY COLUMN(哪怕VARCHAR(100) → VARCHAR(150))、收缩VARCHAR长度、跨字节边界扩容(如VARCHAR(255) → VARCHAR(256))都会退化为LOCK=SHARED甚至COPY - 一旦退化为
COPY,就是全程MDL_EXCLUSIVE,DML彻底阻塞
生产环境更稳妥的替代方案
5.7原生Online DDL的“在线”是带条件的,真要零感知变更,得绕开限制:
- 用
pt-online-schema-change:通过触发器同步增量,只在最后RENAME时短暂锁表;但要注意触发器可能加重高更新表的锁争用 - 用
gh-ost:基于binlog解析增量,不依赖触发器,但要求binlog_format=ROW且row_image=FULL(5.7+默认满足) - 从库先行:先在从库执行DDL,验证耗时与资源占用,再选低峰期切主库
真正容易被忽略的是:哪怕你用了pt-osc或gh-ost,如果原表有未提交的长事务,它们在启动时仍会因无法获取MDL_SHARED而卡住——所以检查活跃事务这一步,永远不能省。











