Files
docs/数据库版本迁移与Jenkins自动发布方案.md
T

478 lines
17 KiB
Markdown
Raw Normal View History

2026-09-18 16:48:32 +08:00
# 数据库版本迁移与 Jenkins 自动发布方案
## 1. 文档状态与目标
- 状态:方案已评审,待分阶段实现。
- 适用环境:共享测试环境、国内生产环境、海外生产环境。
- 适用数据库:MySQL 8。
- 迁移工具:Flyway。
- 发布入口:Jenkins。
目标是让每次上线自动执行当前代码版本所需的数据库变更,人工只负责代码评审、发布审批和上线验收,不再登录服务器手工执行 SQL。
本方案只定义后续版本迁移机制,不重新执行现有初始化 SQL,不授权重置、清空或修复任何环境的数据。
## 2. 核心结论
1. 现有两份统一 SQL 只作为空库初始化基线,不能在已有数据库重复执行。
2. 后续数据库变化必须新增不可变的版本迁移文件,禁止修改已经在共享测试或生产执行过的文件。
3. 国内和海外使用独立目录、独立数据库连接、独立 Flyway 历史表,禁止跨区域或跨 schema 混合执行。
4. Jenkins 每次部署都执行 `info -> validate -> migrate -> validate`,不能仅依赖 Git diff 判断是否需要迁移。
5. 发布顺序为:构建并推送全部镜像 -> 执行数据库迁移 -> 部署同一批已构建镜像 -> 健康检查和人工验收。
6. 测试环境允许频繁发版,但已执行迁移同样不可修改;修正必须新增更高版本。
7. 测试重置是独立任务,生产环境永远不允许自动 `clean`、重建或回滚数据库。
## 3. 当前迁移起点
### 3.1 已执行的初始化基线
生产环境已经分别执行:
- 国内:`头程数据库统一增量-MySQL8.sql`
- 海外:`海外区域统一增量-MySQL8.sql`
当时验收结果:
- 国内初始化通过,共 208 张表。
- 海外初始化通过,共 137 张表。
生产执行文件的 SHA-256:
- 国内:`27fbb98c603f7be1681eb4672b0e8d3010944e339fb64c0dfc00fa89b99ef036`
- 海外:`df91d199b84d02509cbe1f9a3a50a44e209e8c746a94ca40da7db32858d6d3a5`
当前仓库内同名文件与生产执行版本存在漂移风险。因此在启用自动迁移前,必须先从生产部署留档或当时上传文件中找回上述精确版本,以哈希复核后冻结为基线。不能直接把当前本地同名文件认定为生产基线。
### 3.2 国内数据库边界
国内区域当前包含:
- `baiyue_auth`
- `baiyue_iam`
- `baiyue_order`
- `baiyue_warehouse`
- `baiyue_firstleg`
- `baiyue_file`
- `baiyue_audit`
- `baiyue_event_bridge`
- `baiyue_finance`
### 3.3 海外数据库边界
海外区域当前包含:
- `baiyue_lastleg`
- `baiyue_lastlag_warehouse`
- `baiyue_event_bridge_overseas`
`baiyue_lastlag_warehouse` 的现有拼写视为当前数据库事实。若后续决定改名,必须作为单独的受控迁移处理,不能在引入 Flyway 时顺带修改。
## 4. 推荐目录结构
```text
baiyue-firstleg/
└─ deploy/
└─ sql/
├─ README.md
├─ VERSION
├─ baseline/
│ ├─ cn/
│ │ └─ V0.0.0__cn_baseline.sql
│ └─ overseas/
│ └─ V0.0.0__overseas_baseline.sql
├─ migrations/
│ ├─ cn/
│ │ ├─ baiyue_auth/
│ │ ├─ baiyue_iam/
│ │ ├─ baiyue_order/
│ │ ├─ baiyue_warehouse/
│ │ ├─ baiyue_firstleg/
│ │ ├─ baiyue_file/
│ │ ├─ baiyue_audit/
│ │ ├─ baiyue_event_bridge/
│ │ └─ baiyue_finance/
│ └─ overseas/
│ ├─ baiyue_lastleg/
│ ├─ baiyue_lastlag_warehouse/
│ └─ baiyue_event_bridge_overseas/
└─ scripts/
├─ validate-migrations.*
└─ migrate-region.*
```
约束:
- 一个迁移文件只能修改一个 schema。
- 每个 schema 使用自己的 `flyway_schema_history`。
- 国内任务不能获得海外数据库连接,海外任务不能获得国内数据库连接。
- `VERSION` 只声明当前代码对应的数据库发布版本,例如 `0.0.10`,不能在 Jenkins 页面由操作人临时输入。
- 服务模块下的 `resources/db/` 可以继续作为最终结构或开发参考,但上线迁移的唯一事实来源必须是 `deploy/sql/migrations/`。
## 5. 基线规则
### 5.1 新建空库
新建空库时执行已冻结的 `V0.0.0` 基线,再执行所有高于 `0.0.0` 的版本迁移。
### 5.2 已初始化数据库
生产和现有共享测试库已经具备基线结构,接入 Flyway 时只做一次受控登记:
1. 备份数据库并确认可恢复。
2. 对照基线校验关键 schema、表和索引。
3. 复核基线文件 SHA-256。
4. 将数据库标记为基线版本 `0.0.0`。
5. 从 `V0.0.1.1` 开始执行后续迁移。
严禁为了建立历史表而重新执行完整初始化 SQL。
### 5.3 基线不可变
一旦 `V0.0.0` 用于任何共享环境:
- 禁止编辑、覆盖、重新生成或重命名。
- 若发现基线缺陷,新增正常版本迁移修正。
- 基线文件与其 SHA-256 必须永久留档。
## 6. 版本号和文件命名
业务发布版本使用三段数字:
```text
0.0.1 -> 0.0.2 -> 0.0.10 -> 0.1.0 -> 1.0.0
```
同一业务版本允许多条迁移,通过第四段递增:
```text
V0.0.1.1__create_login_audit_table.sql
V0.0.1.2__add_user_last_login_at.sql
V0.0.2.1__add_order_declared_value.sql
V0.0.10.1__create_export_task_index.sql
```
规则:
- Flyway 按数字段比较,`0.0.10` 正确大于 `0.0.2`。
- 同一仓库内版本必须唯一且严格递增。
- 文件名只使用 ASCII 小写字母、数字和下划线。
- 描述应说明业务动作,不能使用 `fix.sql`、`new.sql`、`final.sql` 等模糊名称。
- `deploy/sql/VERSION` 必须等于本次发布版本。
- 一个版本可以没有数据库变更;此时仍执行 `validate`,不会重复执行旧迁移。
示例:
```text
deploy/sql/VERSION
0.0.10
```
版本号是数据库迁移顺序,不应使用时间戳、Git 提交号或 Jenkins 构建号替代。镜像仍应使用不可变 Git commit 标签,以便代码和数据库发布记录相互追踪。
## 7. 迁移类型
### 7.1 版本迁移
业务表结构和受控数据变更统一使用 `V...__...sql`:
- 每个版本只执行一次。
- 执行后保存校验和。
- 执行过的文件不得修改。
### 7.2 可重复迁移
第一阶段不建议使用 `R__...sql`。视图、存储过程等确有需要时再单独引入,因为内容变化会触发再次执行,评审和回滚要求更高。
### 7.3 禁止内容
自动迁移文件中禁止:
- `DROP DATABASE`、`CREATE DATABASE`、切换到其他区域 schema。
- 无保护的 `DROP TABLE`、`TRUNCATE` 或全表删除。
- 密码、Token、云账号密钥和真实客户数据。
- 测试造数、开发演示数据或测试环境专用数据。
- MySQL 客户端的 `--force` 或忽略错误逻辑。
- 在单个文件中同时修改多个 schema。
- 依赖当前时间或不稳定查询结果生成不可重复状态。
## 8. 生产兼容策略:Expand / Contract
数据库迁移通常不能像应用镜像一样直接回滚。生产发布必须采用向前兼容方式。
### 8.1 Expand:先扩展
允许自动执行的典型变化:
- 新增表。
- 新增允许为空或带安全默认值的字段。
- 新增索引,但需要提前评估锁表时间和磁盘空间。
- 新增不破坏旧代码的枚举值或配置数据。
应用发布前,新旧应用版本都必须能够在扩展后的结构上运行。
### 8.2 Migrate:迁移或回填
大量历史数据回填不应塞进普通启动迁移:
- 小规模、可快速完成且可验证的数据修正可以使用版本迁移。
- 大规模回填必须使用独立、可观测、可限速、可续跑的任务。
- 回填完成前不能删除旧字段或旧索引。
### 8.3 Contract:最后收缩
删除字段、改字段类型、收紧非空、重建大索引等破坏性操作,必须在旧代码完全下线并经过观察期后,于后续版本单独执行。
因此应用回滚通常只回滚镜像,不自动回滚已经成功执行的扩展型数据库迁移。
## 9. Jenkins 自动发布流程
### 9.1 总体顺序
```text
合并到 main
-> 静态检查与测试
-> 构建全部目标服务镜像
-> 推送不可变 commit 标签
-> 数据库迁移预检
-> 国内/海外迁移
-> 数据库迁移复验
-> 部署同一批预构建镜像
-> 健康检查
-> 人工业务验收
```
数据库迁移失败时,应用部署必须停止。不能出现部分服务已经上线、随后才发现数据库迁移失败的情况。
### 9.2 构建与部署解耦
当前总控和单服务 Job 需要支持等价于以下两种模式:
- `BUILD_ONLY`:构建并推送镜像,不部署。
- `DEPLOY_ONLY`:只部署指定的不可变镜像标签,不重新构建。
总控流程:
1. 计算并固定本次 Git commit 镜像标签。
2. 调用所有目标服务执行 `BUILD_ONLY`。
3. 确认所有镜像均已推送成功。
4. 执行数据库迁移。
5. 数据库迁移成功后,对同一批镜像执行 `DEPLOY_ONLY`。
这样可以避免迁移成功后才开始构建、或部分服务使用不同提交版本。
### 9.3 每次部署都要运行的迁移步骤
不能只在 Git diff 命中 `deploy/sql/` 时执行。每次实际部署都运行:
```text
flyway info
flyway validate
flyway migrate
flyway validate
```
原因:
- 重试发布时 Git diff 可能为空,但数据库可能尚未迁移。
- 手工重跑旧构建时仍需确认数据库历史一致。
- `migrate` 对已执行版本是幂等的,会跳过而不会重复执行。
Git diff 可以用于显示“本次包含数据库文件变化”和加强审批,但不能作为是否运行迁移的唯一条件。
### 9.4 国内与海外任务
建议拆分为两个明确的 Jenkins stage 或两个下游 Job:
- `db-migrate-cn`
- `db-migrate-overseas`
每个任务只加载本区域:
- 数据库地址。
- 迁移专用账号密码。
- schema 清单。
- 对应迁移目录。
若一次发布同时影响国内和海外,两个区域迁移必须全部成功后才能部署任何依赖这些变更的应用。第一阶段建议串行执行,日志更清楚,失败处理也更简单。
### 9.5 Flyway 运行环境
- 使用固定版本的 Flyway Docker 镜像,禁止 `latest`。
- 镜像先同步到当前已验证可访问的内部镜像仓库。
- Jenkins 仅挂载只读 SQL 目录。
- 数据库密码从 Jenkins Credentials 注入,不进入 Git、构建参数默认值或控制台明文日志。
- 连接必须启用适合生产的 TLS 设置,并明确字符集与时区。
## 10. 测试环境频繁执行策略
### 10.1 共享测试库
共享测试库遵循与生产相同的不可变迁移规则:
- 一个版本只执行一次。
- 修改已执行文件会导致 `validate` 失败。
- 发现错误后新增更高版本修正,例如从 `V0.0.10.2` 修正到 `V0.0.10.3`。
这正是测试环境应提前发现的问题,不能通过修改历史文件掩盖。
### 10.2 开发者临时库
仅个人本地或一次性临时数据库可以删除后从基线重建。它不代表共享测试环境策略。
### 10.3 测试重置任务
如确需频繁重置,建立独立 Jenkins Job,例如 `db-reset-test`,并满足:
- 环境写死为 `test`,不能由参数切换成 `prod`。
- 主机、实例和 schema 使用精确白名单。
- 数据库名必须匹配测试前缀和预期清单。
- 使用测试专用凭据,凭据本身没有生产权限。
- 执行前显示目标并进行明确确认。
- 重置后执行基线、全部迁移和独立测试造数。
生产 Job 中不提供 `clean`、`reset`、`repair` 按钮或参数。
## 11. 提交前静态门禁
当 `deploy/sql/` 变化时,流水线至少检查:
1. 文件路径属于已登记的区域和 schema。
2. 文件名符合 `V数字.数字.数字.序号__描述.sql`。
3. 新版本大于仓库已有版本,且不存在重复版本。
4. 已发布版本文件没有被修改、删除或重命名。
5. `deploy/sql/VERSION` 不小于最大迁移版本,且与发布元数据一致。
6. 文件只影响所属 schema,不出现跨库限定名。
7. 不包含禁止语句、密钥或真实客户数据。
8. SQL 以 UTF-8 保存,结构和注释符合仓库数据库规范。
9. 国内和海外目录没有相互引用。
静态扫描只是门禁,不代替在临时 MySQL 8 数据库执行完整迁移测试。
## 12. 数据库账号与权限
应用账号与迁移账号必须分离。
### 12.1 应用账号
只授予运行期所需的 DML 权限,不应具备建表、删表、修改结构等 DDL 权限。
### 12.2 迁移账号
只由 Jenkins 使用,按区域和 schema 授予完成受控迁移所需的最小权限。国内、海外建议使用不同凭据。
### 12.3 管理员账号
RDS 高权限管理员账号不应长期配置在 Jenkins 中。紧急人工恢复时使用受审计的临时授权流程。
## 13. 失败、恢复与重跑
### 13.1 迁移前失败
镜像已经构建但迁移尚未开始时,直接停止发布。未部署的镜像保留以便排查或重试。
### 13.2 迁移执行失败
1. 立即停止后续应用部署。
2. 保存 Flyway 输出、数据库错误、版本、区域、schema 和 Git commit。
3. 确认 MySQL 是否已经提交了部分 DDL;不能假定整个 SQL 文件自动回滚。
4. 人工判断数据库真实状态。
5. 通过新的更高版本迁移向前修复。
禁止自动执行 `flyway repair`。只有确认数据库状态与预期完全一致后,才允许由数据库负责人进行受审计的人工修复。
### 13.3 迁移成功、应用失败
- 若迁移属于 Expand 类型,回滚应用镜像,数据库结构保留。
- 修复应用后使用同一数据库版本重新部署。
- 若迁移不是向后兼容的,则说明变更未满足本方案门禁,不应进入生产自动发布。
### 13.4 数据库回滚
自动流水线不执行逆向 SQL。严重事故依赖:
- RDS 备份和时间点恢复能力。
- 发布前备份或快照策略。
- 新增向前修复迁移。
- 经人工审批的恢复方案。
## 14. 日志与验收证据
每次迁移至少记录:
- Jenkins 构建号。
- Git commit 和镜像标签。
- `deploy/sql/VERSION`。
- 环境与区域。
- schema 名称。
- Flyway 镜像版本。
- 迁移前、迁移后版本。
- 本次实际执行的迁移文件列表及校验和。
- `validate` 结果和最终退出码。
日志中必须遮蔽数据库密码、Token 和连接串敏感参数。
应用部署完成后,人工只需要验收:
- Jenkins 数据库阶段全部通过。
- Flyway 历史表与目标版本一致。
- 服务健康检查通过。
- 国内、海外关键业务路径可用。
## 15. 分阶段实施清单
### 阶段 A:冻结事实
- 找回生产实际执行的两份初始化 SQL。
- 校验上述生产 SHA-256。
- 归档为不可变 `V0.0.0` 基线。
- 对生产和测试数据库做一次结构核对。
### 阶段 B:建立迁移目录与校验脚本
- 新增 `VERSION`。
- 建立国内、海外、schema 级目录。
- 增加文件名、版本、路径、禁止语句和历史不可变检查。
- 在临时 MySQL 8 中验证从空库到最新版的完整迁移。
### 阶段 C:接入共享测试
- 为各 schema 建立 Flyway 历史表并登记 `0.0.0`。
- Jenkins 测试发布接入 `info/validate/migrate/validate`。
- 验证重复发布不重复执行迁移。
- 验证失败阻止应用部署。
### 阶段 D:改造生产流水线
- 单服务 Job 支持构建与部署解耦。
- 总控先构建全部镜像,再执行迁移,最后部署。
- 配置国内、海外迁移专用凭据。
- 保留现有生产确认门禁;是否取消发布确认应另行决定,不和数据库迁移改造绑定。
### 阶段 E:生产启用
- 先选一个只包含安全新增的版本演练。
- 核对 RDS 备份和恢复能力。
- 国内、海外按发布范围执行迁移。
- 完成健康检查与人工业务验收。
## 16. 首次落地的验收标准
首次启用自动迁移必须同时满足:
- 生产基线文件哈希已确认,没有使用漂移的本地副本。
- 国内和海外历史表相互独立。
- 任一迁移失败都会阻断所有依赖服务的部署。
- 重跑同一 Jenkins 构建不会重复执行成功迁移。
- 篡改已执行文件会被 `validate` 阻止。
- 测试重置任务无法连接生产数据库。
- Jenkins 日志不泄露凭据。
- 部署使用构建阶段确定的同一批不可变镜像。
## 17. 官方参考
- [Flyway Versioned migrations](https://documentation.red-gate.com/fd/versioned-migrations-273973333.html)
- [Flyway Repeatable migrations](https://documentation.red-gate.com/fd/repeatable-migrations-273973335.html)
- [Flyway Schema history table](https://documentation.red-gate.com/fd/flyway-schema-history-table-273973417.html)
- [Flyway Validate](https://documentation.red-gate.com/flyway/reference/commands/validate)