Files
docs/数据库版本迁移与Jenkins自动发布方案.md
T
2026-09-18 16:48:32 +08:00

478 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库版本迁移与 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)