← 中文资源

简体中文版本

用向后兼容性门禁约束数据库迁移

在迁移执行前用 lint 拦截破坏性变更,并采用 expand/contract 分两次发布,确保新旧代码可以同时运行。

阅读英文原文

概述

代码几分钟就能替换,而数据库结构变更会长期存在。滚动发布期间以及任何一次回滚中,两个应用版本会同时访问同一个数据库。如果在同一版本中删除或重命名列,就破坏了这种共存,因为旧版本仍然引用旧结构。安全做法是 expand/contract:先增加新结构,部署同时写新旧两处的代码,回填数据,切换读取,最后在后续版本中移除旧结构。

步骤

  1. 对即将执行的迁移做 lint:
atlas migrate lint --dir file://migrations \
  --dev-url "docker://postgres/16/dev?search_path=public" --latest 1
  1. 拆分为扩展版本与清理版本:
-- 版本 1:新增列,旧代码与新代码都能工作
ALTER TABLE orders ADD COLUMN status_v2 text;

-- 版本 2:旧代码完全下线后再删除旧列
ALTER TABLE orders DROP COLUMN status;
  1. 检查迁移将要对当前数据库执行的 SQL:
atlas schema diff --from "$DATABASE_URL" --to file://migrations
  1. 先在预发执行迁移,并同时运行新旧两套测试:
atlas migrate apply --dir file://migrations --url "$STAGING_DATABASE_URL"

验证

lint 应对 DROP COLUMN、DROP TABLE、无默认值的 NOT NULL 新列等破坏性语句报错;拆分为扩展与清理后应通过。schema diff 的输出在当前版本中应只包含新增语句。最终验证是让旧版本测试套件与新版本测试套件都跑在迁移后的库上并通过,这是向后兼容最直接的证明。

注意事项

  • lint 无法证明数据正确,回填仍需要校验查询与行数核对。
  • 大表上的在线 DDL 受引擎限制,可能需要专门的迁移工具与维护窗口。
  • 迁移应设置较短的 lock_timeout 并重试,避免长时间阻塞业务查询。
  • 回填应放在可重入的独立任务中,失败后可以继续而不用长事务。
  • 删除旧结构应至少滞后两个发布周期,确保回滚窗口已过。