SQL Flyway 的 cherry-pick migration 与分支合并变更处理

SQL Flyway 的 cherry-pick migration 与分支合并变更处理

Flyway 本身不支持 cherry-pick 式的迁移(即选择性应用某几个特定 migration 文件),也不原生处理多分支开发中迁移脚本冲突或重复应用的问题。它的设计哲学是“线性、不可变、按序执行”,因此所谓“cherry-pick migration”需通过外部流程和约定来规避风险,而非靠 Flyway 内置功能实现。

为什么 Flyway 不支持真正的 cherry-pick

Flyway 的核心机制依赖 flyway_schema_history 表严格记录已执行 migration 的 versiondescriptionchecksum。一旦某个版本被标记为 success,Flyway 就不会再执行它——无论该脚本是否被手动复制、重命名或挪到另一分支。这意味着:

  • 直接拷贝一个已执行过的 V1.2__add_user_status.sql 到新分支并试图运行,会报错 “Detected resolved migration not applied to database”
  • 修改已有脚本内容会导致 checksum 不匹配,Flyway 拒绝执行并提示校验失败(除非用 repair,但这是高危操作)
  • 没有“跳过中间版本只执行 A 和 C”的语法或命令

多分支场景下迁移变更的实用处理方式

当团队使用 Git 分支并行开发(如 feature/user-service、feature/order-module),且各自引入数据库变更时,关键在于统一协调,而非技术上“挑着执行”。推荐做法包括:

  • 所有 migration 必须走主干(main/mainline)合并:禁止在 feature 分支直接提交 migration 脚本;所有 DDL/DML 变更需先合入 main,再由 CI 触发 Flyway migrate
  • 使用语义化版本 + 时间戳前缀避免冲突:例如 V202405201430__add_user_status.sql,比单纯 V1.2 更易识别顺序与来源
  • 本地开发用独立 schema 或容器化 DB:每个开发者启动自己的 PostgreSQL 容器并绑定专属 schema,避免本地 migration 影响他人,也降低误执行风险
  • 对紧急 hotfix 迁移,单独建 hotfix/ 分支并立即合入 main:不跨分支 cherry-pick 脚本,而是让该脚本成为下一个全局可部署版本的一部分

不得已需要“类 cherry-pick”时的临时方案

仅限开发/测试环境、数据无损要求低的场景,且必须人工确认安全:

  • 手动插入 flyway_schema_history 记录:在目标库中 INSERT 一条对应 migration 的记录(state=’SUCCESS’),跳过执行。适用于已知该脚本不会引发结构冲突,且你确信其逻辑已在别处生效
  • flyway repair 清理校验状态:当误改脚本内容后 checksum 失败,且确认改动合理,可用 repair 更新 checksum。注意:repair 不执行脚本,只修正元数据,务必配合 -dryRunOutput 预览
  • 导出 SQL 片段临时执行:从待选 migration 文件中提取实际 DDL/DML 语句,在目标库中手工运行,并手动更新 flyway_schema_history。这脱离 Flyway 管控,仅作应急,不可用于生产

替代方案:考虑 Liquibase(如需灵活变更管理)

若团队频繁需要分支隔离、脚本回滚、条件执行或 cherry-pick 场景,Liquibase 的 changeset id + author + context 模型更适配:

  • 支持 ./liquibase update --includeContexts=dev 按上下文筛选执行
  • 可通过 changelogSync 手动注册已存在变更,模拟 cherry-pick 效果
  • 提供 rollbackfutureRollbackSQL,对非线性演进更友好

不过迁移成本和学习曲线更高,建议仅在 Flyway 的约束已明显阻碍协作节奏时评估切换。