资讯文章 资讯 · NEWS
大表加个字段要停多久:目录站的在线改表该怎么选
2026-10-24 约 1,307 字 预计阅读 3 分钟 yiyunkj
大
目录站的数据模型有个特点:一张宽条目表,字段越加越多,索引也越加越多。表小的时候怎么改都行,过了千万行,同样是 ALTER TABLE,代价能差出几百倍。
差别来自 MySQL 选择的算法。共有三档,先弄清楚它在哪一档,再决定能不能直接执行。

图片来源:网络,版权归原作者所有,侵权请联系删除
三档算法,代价完全不同
| 算法 | 做了什么 | 代价 | 典型耗时(千万行) |
|---|---|---|---|
| INSTANT | 只改元数据,不动行 | 几乎为零 | 秒级 |
| INPLACE | 原地重建索引,不建临时表 | 占用 IO 和 CPU,仍需扫描 | 分钟到几十分钟 |
| COPY | 建新表、拷数据、重命名 | 额外磁盘约等于表大小,全程锁写 | 几十分钟到小时级 |
INSTANT 是从 MySQL 8.0.12 开始有的,最早只支持在表尾加列;8.0.29 之后放开到删列和更灵活的位置。8.0.28 起重命名列也能走 INSTANT。好消息是,只要操作支持,MySQL 默认就用 INSTANT,不用显式声明。
所以第一步不是想方案,是先问它这次会用哪种算法。做法是显式写上 ALGORITHM=INSTANT 执行,不支持的话它会直接报错,而不是悄悄降级成 COPY。想验证另一种算法,同样显式指定,让它报出来。
不支持 INSTANT 的时候
改列类型、改字符集、加全文索引这些,INSTANT 帮不上忙。这时候有两条路:接受一次 INPLACE 的代价,或者用工具做影子表迁移。
pt-online-schema-change 和 gh-ost 都属于后者,思路一致:建一张新表改好结构,把老数据分批拷过去,同时把拷贝期间的增量变更同步过去,最后在极短的时间里把两张表换名。两者的区别在增量怎么拿:pt-osc 在老表上建触发器,gh-ost 去读 binlog。
选哪个看环境。触发器方案对表上已有的触发器冲突,也会在有外键的表上麻烦一些;读 binlog 的方案不碰原表,但要求 binlog 格式和权限都配好,对主从延迟更敏感。两个工具都支持限速和暂停,这是它们比直接 ALTER 强的地方——卡住了能退。
执行前要算的三笔账
磁盘:COPY 和大部分 INPLACE 都需要额外空间,量级接近一张表。磁盘剩 20% 的机器上跑大表改结构,是把一次变更变成一次事故的最短路径。
从库延迟:主库上不管什么算法,从库都要重放一遍。大事务会让延迟直线上升,读从库的页面跟着读到旧数据。限速参数就是为此存在的,宁可慢一点。
锁:LOCK=NONE 是能争取的最好情况,但某些操作仍会在开始和结束的瞬间拿元数据锁。有长事务卡在那里的时候,连这个瞬间都过不去,会被后面的查询堵住一片。
顺序和退路
我的执行习惯是:先在从库上跑一遍拿到真实耗时,再挑低峰期上主库;老表改名保留至少一天,别立刻删;改完立刻看一遍慢查询和从库延迟,确认没有跟着变坏。
还有一点,代码和数据库要分开发。先上能兼容两种结构的代码(新字段可空、读的时候有兜底),再改表,最后再上依赖新字段的逻辑。反过来做的话,中间那几分钟页面一定报错。
索引本身该不该加、怎么组合,和这次改结构是两件事,之前在条目表复合索引那篇里单独写过。
加字段本身不是难事,难的是没人知道这次会花多久。把"这次用哪种算法、预计多久、需要多少磁盘"写成一条上线前必须回答的问题,事故率会立刻降下来。