Appearance
大表在线 DDL 怎么做更稳
大表变更是数据库治理里最容易“出一次事故就记一辈子”的主题。
因为它表面看起来只是一个 alter table,但一旦判断失误,就可能引发:
- 元数据锁把业务 SQL 全部堵住
- 主从延迟拉长
- binlog 激增
- 磁盘和 IO 压力陡升
- 发布窗口被迫回滚
先说结论
- 大表 DDL 的核心不是“语法能不能执行”,而是“变更期间业务、复制和回滚是否可控”
- 先判断是否支持真正的在线变更,再决定走原生 DDL、影子表工具还是灰度双写
- 元数据锁、主从延迟和磁盘空间是大表变更最常见的三个事故点
- 真正稳的做法一定包含预检查、低峰执行、观测和回滚预案
先分清你在改什么
大表 DDL 不能一概而论。
不同变更类型,风险差异非常大。
常见分成三类:
1. 元数据级轻变更
比如部分版本下的字段重命名、默认值调整、注释变更。
这类可能很快,但仍可能短暂拿元数据锁。
2. 支持在线算法的结构变更
例如某些加索引、加列、修改表选项,可以走 ALGORITHM=INPLACE 或更轻的能力。
3. 需要重建表的数据级重变更
比如:
- 改字段类型
- 改主键
- 重排序列
- 某些不支持在线算法的操作
这类往往风险最大,可能伴随整表重建。
为什么大表变更容易出事故
因为它不只是数据库内部慢一点,而是会牵动整条链路。
1. 元数据锁会堵住正常业务
很多人以为 DDL 卡住只是自己那条命令慢。
实际上 DDL 需要拿元数据锁,如果前面有长事务没释放,DDL 会等待;而它等待期间,后续对这张表的新 SQL 也可能被堵在后面。
这就是线上最典型的“alter 一发,全站慢”。
2. 表重建会带来大量 IO 和 binlog
如果变更需要拷表或重写数据页:
- 磁盘写放大
- redo / binlog 压力升高
- 主从复制延迟扩大
3. 风险不是只在主库
主库变更成功不代表没事。
从库回放 DDL 慢、磁盘不足、复制中断,都会让风险延后爆发。
原生在线 DDL 不要盲信
很多文章会说 MySQL 8 支持在线 DDL,于是团队就觉得可以放心直接改。
这很危险。
你至少要先确认:
- 当前 MySQL 版本
- 当前引擎
- 当前操作是否真的支持在线算法
- 是否仍会拿 MDL
- 是否仍会重建表
“支持 online”不等于“完全无感”。
常见更稳的三种做法
1. 原生 DDL + 明确算法和锁级别
适合本身支持轻量在线变更的操作。
优势是链路最短,但前提是你要足够确定风险边界。
2. 影子表工具
比如 pt-online-schema-change、gh-ost 这类思路:
- 建新表
- 增量同步数据
- 最后切换表
它们的价值在于把“重变更”拆成更可控的渐进过程。
3. 应用层灰度改造
当字段变更对业务语义影响很大时,更稳的方式常常不是一次性 DDL,而是:
- 先加新列
- 应用双写
- 回填历史数据
- 读流量灰度切换
- 最后清理旧列
这类方式最啰嗦,但对核心表最稳。
一套实战前检查清单
大表变更前,我一般会先看这些:
- 表行数、索引数、磁盘大小
- 当前是否有长事务
- 当前峰值 QPS 和低峰窗口
- 主从延迟和从库磁盘空间
- 变更是否支持在线算法
- 有没有回滚方案
如果上面任何一项不清楚,就不要直接在生产上动。
上线过程要重点盯哪些指标
执行过程中至少要盯:
- DDL 执行状态
- 活跃事务和 MDL 等待
- 主从延迟
- 磁盘空间
- 业务 RT 和错误率
很多团队只盯数据库命令本身,忽略业务侧指标,结果问题已经扩散了才发现。
最容易踩的几个坑
1. 发布前不清长事务
前面一个长事务占着表,DDL 一直拿不到锁。
你以为是“DDL 很慢”,其实是元数据锁排队把正常流量也堵了。
2. 只在主库评估,不看从库容量
主库挺住了,但从库回放期间磁盘打满或延迟爆炸,一样会出事故。
3. 没有分步骤的回滚设计
字段删了、索引删了、表切了之后再想回滚,成本会非常高。
4. 把所有变更堆到一个窗口
大表加列、索引调整、数据回填、应用发布一起做,风险会成倍放大。
一个更稳的变更思路
对于核心业务大表,通常推荐:
- 先做只增不删的兼容性变更
- 应用先兼容新旧结构
- 数据回填独立执行
- 灰度读写切换
- 最后做收尾清理
这样虽然步骤多,但每一步都可观测、可暂停、可回滚。
总结
大表在线 DDL 不是数据库语法问题,而是一次高风险生产变更。
真正决定成败的,不是你会不会写 alter table,而是你是否提前想清楚:
- 它会不会重建表
- 会不会堵住业务
- 从库扛不扛得住
- 失败后怎么收回来