Schema 版本化迁移与在线 DDL
表结构变更(DDL)应像代码一样版本化、可评审、可回放。手工执行 ALTER TABLE 是生产事故的高发区,尤其在大表上。本章讲清迁移工具、演进模式与在线变更。
为什么需要版本化迁移
- 可追溯:每个环境(开发/测试/生产)的结构一致,来源清晰。
- 可重放:新环境能一键从零建库到最新版本。
- 可评审:DDL 与代码一起走代码评审与 CI。
- 可回滚:出现问题时有明确的回退路径。
迁移工具
以 golang-migrate 为例:
目录结构遵循「成对」约定:
在 Go 代码中嵌入并在启动时迁移:
Warning
迁移应在部署流程中由单一实例串行执行,并加分布式锁或迁移表防止多实例并发迁移。多数工具会在数据库中记录版本表(如 schema_migrations),天然提供幂等与顺序保证。
expand-contract:安全的结构演进
直接「改列名/改类型」会同时破坏新旧代码。安全的做法是分阶段演进,让新旧版本共存:
每一步都可独立发布与回滚,避免「一次性大迁移」带来的停机和不可逆风险。
大表在线 DDL
直接 ALTER TABLE 在大表上可能长时间锁表。对策:
Tip
判断 DDL 是否会重写表:MySQL 中改列类型、改字符集、加索引通常需要重建;加/删列、改默认值在 MySQL 8 多为 INSTANT。执行前用 ALGORITHM=INPLACE, LOCK=NONE 显式要求,从报错中确认可否在线。
回滚与向前修复
- 优先向前修复:多数团队只维护
up迁移,出错时用新迁移修正,而不是down。 down的陷阱:数据迁移(如拆分、回填)通常不可逆,down只能撤销结构,无法还原数据。- 有损变更要谨慎:删列、改类型、截断,务必先备份并在低峰执行。
- 迁移与发布解耦:先迁移(向后兼容)→ 再发布新代码 → 最后清理旧结构。
Warning
永远不要在生产手工执行未经评审、没有备份、没有回滚方案的 DDL。高危操作包括:DROP、TRUNCATE、改主键、改字符集、无 WHERE 的 UPDATE。生产变更应走工单 + 审核 + 低峰窗口 + 备份。
跨库与分片下的迁移
- 分库分表意味着同一张逻辑表有多个物理表,迁移需遍历所有分片(工具需支持批量)。
- 变更必须保持各分片结构一致,否则中间件路由会出错。
- 跨版本/异构迁移要同步核对字符集、时区、排序规则、
sql_mode、序列等差异。
小结
- Schema 像代码一样版本化:工具 + 成对 up/down + CI 评审。
- 用 expand-contract 分五步安全演进,新旧版本共存、逐步切换。
- 大表用 Instant DDL / gh-ost /
CREATE INDEX CONCURRENTLY实现在线变更。 - 优先向前修复;有损变更先备份;迁移与代码发布解耦。
- 分片场景需遍历全部分片并保持结构一致。