alter table卡住几小时主因是重建表流程:创建临时表、逐行拷贝、重建索引、原子重命名,全程持mdl与写锁,阻塞所有dml;大表i/o密集,ssd+64gb下千万级耗时2.3–8.7小时,且磁盘不足或长事务会加剧阻塞。

为什么 ALTER TABLE 会卡住几个小时?
因为 MySQL 对大表执行多数 ALTER TABLE 操作时,默认走「重建表」流程:创建临时表 → 逐行拷贝数据 → 重建所有索引 → 原子性重命名。这个过程全程持有 MDL(metadata lock) 和表级写锁,任何 SELECT、INSERT、UPDATE、DELETE 都会被阻塞,直到切换完成。
哪怕只是把 varchar(255) 改成 varchar(1024),只要触发存储格式变更(比如从单字节长度头升级为两字节)、字符集转换(utf8mb3 → utf8mb4),或字段类型本质变化(int → bigint),MySQL 5.7 及更早版本 100% 强制重建;8.0.12+ 虽支持部分就地修改,但类型变更类操作仍无法跳过物理重写。
-
SHOW PROCESSLIST中看到大量Waiting for table metadata lock,大概率是上游长事务没提交,阻塞了 DDL 的 MDL 获取 - SSD + 64GB 内存环境下,千万级表重建耗时常在 2.3–8.7 小时,I/O 密集型操作占主导
- 临时空间占用 ≈ 原表大小 × 2,磁盘不足会直接失败
ALGORITHM=INPLACE 和 LOCK=NONE 真的不锁表吗?
不是“完全不锁”,而是“降级为轻量级锁”——它只在关键阶段(如元数据更新、索引构建末尾)加短暂 MDL,读写请求可并发执行,但仍有风险。
能否真正启用 INPLACE,取决于操作类型和版本:
- 添加普通字段(
ADD COLUMN)在 MySQL 5.6+ 支持ALGORITHM=INPLACE;但若字段含NOT NULL DEFAULT且表非空,仍需回填默认值,可能退化为重建 - 添加索引(
ADD INDEX)在 5.6+ 默认尝试INPLACE,但唯一索引/全文索引/空间索引不保证全程无锁 -
LOCK=NONE仅对读写完全并发有效;若指定LOCK=SHARED,则写入仍被阻塞 - 执行前务必用
EXPLAIN FORMAT=JSON查看实际执行计划,确认是否真走inplace
为什么 pt-online-schema-change 是生产首选?
它绕开了 MySQL 原生命令的锁机制,用“影子表 + 触发器 + 数据同步”模拟在线变更,核心逻辑是:建新表 → 增量复制数据 → 用触发器捕获原表变更 → 最后原子切换表名。
优势明显,但代价清晰:
- 全程不阻塞原表读写,适合 24×7 高可用场景
- 支持限速(
--max-load、--critical-load),避免拖垮主库 - 要求原表必须有主键或唯一非空索引,否则无法可靠同步增量
- 触发器本身有开销,大流量下可能堆积延迟;切表瞬间仍有毫秒级写入抖动
- 不能用于系统表、临时表,也不支持外键依赖复杂的表
如何判断一次 ALTER 是否安全?
别靠经验猜,直接查 MySQL 的执行反馈和日志:
- 执行前先跑
SELECT COUNT(*) FROM your_table确认数据量级;超 500 万行就该警惕 - 用
SHOW CREATE TABLE your_table检查当前字符集、行格式(ROW_FORMAT=DYNAMIC更友好)、是否有全文索引等高风险配置 - 在测试库用
ALTER ... ALGORITHM=INPLACE, LOCK=NONE试跑,观察performance_schema.table_lock_waits_summary_by_table是否出现等待 - 线上执行时,实时监控
information_schema.INNODB_TRX和PROCESSLIST,发现长事务立即干预
最易被忽略的一点:DDL 不可 kill,也不可回滚。一旦开始重建,只能等它完成或 OOM 失败——所以预估时间、预留窗口、准备回退方案(比如提前备份 CREATE TABLE LIKE 结构),比优化 SQL 本身更重要。











