直接执行ALTER TABLE对千万级表改结构几乎必然锁表数分钟至数小时,根本原因是MDL锁阻塞而非配置问题;需用SHOW PROCESSLIST和INNODB_TRX定位长事务,ALGORITHM=INPLACE与LOCK=NONE有严格前提且不规避MDL等待,生产环境应优先采用pt-online-schema-change绕开MDL锁。

直接执行 ALTER TABLE 对千万级表改结构,几乎必然夯住——这不是配置调优能解决的问题,而是 MySQL 5.7 及更早版本的固有行为;8.0.12+ 仅对极少数操作(如末尾加列且无 NOT NULL DEFAULT)真正支持无锁。
为什么卡在 Waiting for table metadata lock
这不是数据行被锁,是元数据锁(MDL)阻塞。只要有一个未提交事务或长时间 Sleep 连接持有该表的 MDL_SHARED_READ,ALTER TABLE 就拿不到必需的 MDL_EXCLUSIVE,只能干等。
-
SHOW FULL PROCESSLIST中看到大量Waiting for table metadata lock,同时伴随一堆Command = Sleep、Time > 300、Info = NULL的连接,基本就是它们 hold 住了 MDL -
SELECT * FROM information_schema.INNODB_TRX WHERE TIME_TO_SEC(NOW() - trx_started) > 60才能定位真正在跑的长事务;SHOW OPEN TABLES WHERE In_use > 0对 MDL 无效,别依赖它 - 哪怕只执行过
SELECT * FROM t没提交,也会阻塞ALTER TABLE
ALGORITHM=INPLACE 和 LOCK=NONE 到底能不能信
能用,但有严格前提,且不等于“完全不锁”。MySQL 不会静默降级,失败就报错。
-
ADD COLUMN在末尾添加、且无NOT NULL DEFAULT值时,5.6+ 可走ALGORITHM=INPLACE;但若表非空又带NOT NULL DEFAULT,仍需全表回填 → 触发重建 -
MODIFY COLUMN类型变更(如VARCHAR(255) → VARCHAR(1024))在 5.7 中 100% 强制重建;8.0.12+ 仅当长度头不变、字符集不变、无存储格式升级时才可能 inplace - 加索引一般支持
LOCK=NONE,但唯一索引 / 全文索引 / 含BLOB字段时可能降级;执行前务必用EXPLAIN FORMAT=JSON确认"alter_algorithm": "inplace"和"supports_inplace": true
生产环境首选:用 pt-online-schema-change 绕开 MDL
它不依赖 MySQL 的在线 DDL 机制,而是新建影子表 + 触发器捕获变更 + 分块拷贝 + 原子切换,整个过程原表读写基本不受影响。
- 执行前必须看它生成的 SQL,尤其注意
RENAME TABLE语句顺序和目标库名是否正确 - 禁止在从库单独运行再切主从——
pt-osc不复制 DDL,会导致主从表结构不一致 - 磁盘空间要预留 ≥ 原表大小 × 1.5,触发器写入和拷贝过程会产生额外日志与临时文件
- 命令示例:
pt-online-schema-change D=your_db,t=your_table --alter "ADD COLUMN new_col INT" --execute
最常被忽略的点是:误以为 LOCK=NONE 就等于“零影响”,其实它只控制数据行锁级别,MDL 锁依然存在;而真正绕开 MDL 的只有 pt-osc 或人工影子表方案——但后者要求业务层能短暂容忍双写或切换窗口。


















