千万级表直接alter table风险极高,mysql online ddl并非完全无锁,pt-osc通过影子表+触发器分批迁移更安全,但受限于外键、已有触发器及高并发写入开销。

千万级表直接 ALTER TABLE 加字段或改类型,基本等于线上抖动甚至雪崩——不是能不能做,而是敢不敢在主库上执行。
MySQL 5.6+ 的 Online DDL 真的“在线”吗?
它只对部分操作真正无锁,且依赖版本、存储引擎、字段定义细节。比如:
-
ADD COLUMN允许ALGORITHM=INPLACE, LOCK=NONE,但前提是字段允许为NULL;加NOT NULL DEFAULT 'xxx'就会触发全表扫描填充,默认值写入过程锁写 -
MODIFY COLUMN改类型是否支持INPLACE,得查 MySQL 官方 INPLACE 支持矩阵;VARCHAR(100) → VARCHAR(200)通常可以,但INT → BIGINT在某些版本仍需拷表 - 即使语法写了
LOCK=NONE,如果从库回放慢,主库ALTER也会被阻塞(因为 DDL 是串行事件) - 磁盘空间必须预留双倍:原表 + 新表结构临时空间,500GB 表至少要 1TB 空闲
pt-online-schema-change(pt-osc)为什么更稳?
它不依赖 MySQL 内核的 Online DDL 能力,而是用“影子表 + 触发器 + 分批迁移”绕过锁表限制,适合绝大多数生产场景。
- 执行前自动检查:主从延迟、负载(
Threads_running)、可用磁盘空间 - 数据迁移按
--chunk-size分批(默认 1000 行),每批事务小,不影响主库 QPS - 增量写入靠触发器捕获:所有对原表的
INSERT/UPDATE/DELETE都实时同步到新表 - 切换瞬间用
RENAME TABLE原子操作,业务感知在毫秒级 - 失败可安全中断,已迁移数据保留,重试时跳过已处理 chunk
典型命令:
pt-online-schema-change \ --alter "ADD COLUMN remark TEXT" \ D=mydb,t=order_info \ --execute \ --chunk-size=5000 \ --max-lag=3 \ --critical-load="Threads_running=40"
哪些情况 pt-osc 也扛不住?
它不是银弹,几个硬伤必须提前识别:
- 原表不能有
ON DELETE CASCADE或ON UPDATE CASCADE外键,否则触发器会冲突 - 表上已有触发器?pt-osc 会拒绝执行,必须先删掉或改用
gh-ost - 高并发写入下,触发器本身有开销,可能抬高 CPU;若单条写入耗时 >100ms,建议压测验证
- DDL 过程中禁止手动
DROP或TRUNCATE原表,否则触发器失效,数据不同步 - 切换完成后,旧表不会自动删,得手动
DROP TABLE order_info_old,否则占空间
最后提醒一句
别只盯着“怎么改”,先问一句:这个字段真得进主表吗? 日志埋点、宽表预计算、ES 同步、列存分析库……很多时候,绕开 DDL 才是最快最安全的路径。千万级表的每一次 ALTER,本质都是在和 IO、锁、复制延迟、运维心智负担博弈——能不动结构,就不动结构。











