工程效率与质量 / 进阶

不停机数据库迁移:用 Expand and Contract 拆掉危险变更

把字段重命名、约束收紧等破坏性操作拆成兼容阶段,降低应用与数据库同时切换的风险。

一次发布不要同时破坏两端

直接重命名生产字段会让旧应用立刻报错,而滚动发布期间新旧实例通常会共存。Expand and Contract 的做法是先添加兼容结构,再迁移读写,最后删除旧结构。每一步都能独立发布和回滚。

Expand:新增字段或表,不删除旧结构;Migrate:双写并回填历史数据;Switch:读取切到新结构并观察;Contract:停止旧写入,最终删除旧结构。

回填要限速、可恢复

批次应足够小,让锁和 WAL 增长可控。记录最后完成的主键,任务中断后可以继续。回填过程中持续观察复制延迟、数据库负载和错误率,必要时自动降速。

EXAMPLE / 02数据库
UPDATE users
SET display_name = nickname
WHERE id > $1
  AND id <= $2
  AND display_name IS NULL;

删除旧结构前先证明它没被使用

停止旧字段写入后,至少跨过一个完整业务周期。通过查询日志、代码搜索和指标确认没有旧版本实例、离线任务或报表还在读取。删除动作独立成一次发布,回滚方案则是重新添加兼容结构或恢复旧读路径。

大表上直接添加带默认值的非空字段可能持有重锁。先加可空字段、分批回填,再以低风险方式验证并收紧约束。