大表alter table必须分层判断:先验原生online ddl是否真生效(如algorithm=inplace受操作类型和版本限制),再选pt-online-schema-change等工具并调优参数,最后须应用层灰度配合。

大表 ALTER TABLE 不能靠“试试看”——只要表行数超过百万、数据量超 10GB,原生命令极大概率会锁表、拖垮写入、触发主从延迟甚至高可用切换。安全的前提是放弃“一条语句搞定”的幻想,转而分层判断:先看能不能用原生 Online DDL,再看要不要切工具,最后必须配合应用层灰度。
判断 ALGORITHM=INPLACE 是否真生效
MySQL 5.7+ 声称支持 Online DDL,但 ALGORITHM=INPLACE 不等于“不锁表”,更不等于“不扫描数据”。是否真正走 inplace,取决于操作类型和列定义细节:
- 添加带
NOT NULL DEFAULT的列:MySQL 8.0.12+ 才支持 INSTANT;8.0.12 之前仍需 INPLACE(扫描全表填充默认值),LOCK=NONE仅在满足约束时才允许 - 修改列类型(如
VARCHAR(50) → VARCHAR(200)):看似只是扩大长度,但如果该列上有索引,且原长度已接近索引长度上限(如 utf8mb4 下 767 字节限制),InnoDB 可能拒绝 inplace,退化为 COPY - 执行前务必查
INFORMATION_SCHEMA.INNODB_TRX确认无长事务;执行中用SHOW PROCESSLIST观察状态是否卡在altering table或copy to tmp table
pt-online-schema-change 的关键参数避坑
pt-online-schema-change 是目前最稳妥的大表改结构方案,但它不是黑盒,几个参数配错反而会放大风险:
-
--chunk-size默认 1000 行,对亿级表太小——会导致大量小事务、binlog 爆涨、主从延迟加剧;建议按单 chunk 耗时 ≤ 0.1s 调整,通常设为 10000~50000 -
--max-load监控的是Threads_running,不是 CPU 或磁盘 IO;若线上常有短时并发高峰,设太严会频繁暂停,拖长总耗时;建议结合SHOW GLOBAL STATUS LIKE 'Threads_running'历史峰值设为 1.5 倍 -
--critical-load是熔断开关,一旦触发会直接退出;生产环境别用默认值,应设为比--max-load高 20%~30%,留出缓冲余量 - 务必加
--dry-run和--print先看它生成的 SQL 和触发器逻辑,确认没误删索引或漏建约束
应用层必须配合的三件事
再好的 DDL 工具也只管数据层,应用层不配合,照样出问题:
- 新增字段后,ORM(如 MyBatis、Hibernate)若用了
SELECT *或未更新实体类,新字段值会被忽略或写入 NULL,后续业务逻辑可能因空值崩掉 - 删除字段前,所有读写接口必须完成下线:包括日志采集、BI 报表 SQL、定时任务脚本里硬编码的字段引用,漏一处就可能报
Unknown column - 字段类型变更(如
INT → BIGINT)后,Java 侧int变量可能溢出;Go 侧int32同理——必须同步升级代码里的类型声明和校验逻辑
真正麻烦的从来不是语法怎么写,而是改完之后谁还在读那个字段、哪个服务还依赖旧长度、binlog 解析器是否认得新类型。DDL 安全的终点不在数据库里,而在所有调用它的代码和配置中。











