一、为什么数据库迁移会出问题?
很多做DevOps的小伙伴肯定踩过数据库迁移的坑——两个开发并行改了用户表的结构,合并脚本时没注意顺序,部署后要么字段缺了,要么类型错了,整个数据库数据乱成一锅粥,找了半天才发现是脚本顺序搞反了。我之前在一个电商团队就遇过类似的事:开发A写了给用户表加信用分字段的脚本,开发B写了把用户id从int改成bigint的脚本,Git合并时没按版本号排序,把改id类型的脚本放到了后面,部署时先给用户加了信用分字段,再改id类型,导致信用分字段的int类型和bigint的id不兼容,整个迁移失败,用户表的数据还丢了一部分,花了一晚上才用备份恢复,耽误了大促前的核心测试。
1.1 脚本顺序乱的本质问题
本质上是迁移脚本的执行没有强制的版本约束:纯靠开发者手动命名脚本(比如加1、2的序号),但并行开发时经常会出现序号冲突或者顺序颠倒,Git合并时又没人刻意检查脚本的执行顺序,流水线部署时就会按文件名的字母顺序跑,完全不管开发者的预期,自然会导致数据不一致,比如字段缺失、类型错误、关联表数据错位等。
1.2 临时解决办法为啥不行
有人可能会说“我手动数脚本的序号,确保按顺序提交”,但这个方法太不靠谱:开发者赶进度时会忘,临时改脚本时会乱序号,CI自动部署时没人盯着,万一哪个环节漏了检查,还是会出问题;而且真出问题要回滚,还得手动删改的字段、改类型,不仅慢,还容易删错备份数据,反而扩大故障。
二、用Flyway+CI解决问题的核心思路
2.1 Flyway是什么?
通俗讲,Flyway就是给数据库迁移脚本做“版本管理的工具”,它强制要求脚本按版本号从小到大执行,而且自带校验功能:只要脚本的顺序不对,它会直接报错,不让流水线继续跑;还能自动生成回滚脚本,出问题时一键把数据库恢复到之前的版本,不用手动改SQL。
2.2 CI的作用
CI就是咱们平时说的“持续集成工具”,比如GitLab CI、GitHub Actions,它会在代码合并到主分支时自动跑脚本校验和迁移,不用人工触发,确保每次部署前都先检查脚本顺序,跑迁移时出问题就自动回滚,完全把人工的活交给机器,避免人为失误。
三、具体落地的实操方案
3.1 示例技术栈
本次示例统一使用Spring Boot 2.7 + Flyway 8 + GitLab CI,单一技术栈,所有配置和脚本都围绕这个生态展开,不需要额外装别的工具。
3.2 Spring Boot中Flyway的核心配置
首先要在项目的配置文件里指定脚本的存放路径、开启校验功能,确保Flyway自动接管迁移流程,配置如下:
# Spring Boot的application.yml配置
spring:
datasource:
url: jdbc:mysql://localhost:3306/devops_db?useSSL=false&serverTimezone=UTC
username: root
password: 你的数据库密码
flyway:
enabled: true # 开启Flyway功能
locations: classpath:db/migration # 迁移脚本存放的目录,必须是src/main/resources/db/migration
validate-on-migrate: true # 核心:自动校验脚本顺序,顺序不对就报错终止
baseline-on-migrate: true # 如果是第一次用Flyway,要开启这个,避免重复执行旧脚本
undo-locations: classpath:db/migration/undo # 回滚脚本的存放目录,可选但必须配置,用于自动回滚
这里要提的是,Flyway的脚本命名有严格规则:必须以V版本号__描述.sql开头,比如给用户表加手机号的脚本叫V2.1__add_user_phone.sql,回滚的脚本叫U2.1__remove_user_phone.sql,两个下划线是必须的,版本号只能是数字加小数点,不能有字母,不然Flyway识别不了。
3.3 GitLab CI的校验与自动回滚配置
接下来要在项目根目录下创建.gitlab-ci.yml,让CI每次合并代码到开发分支时自动检查脚本顺序,跑迁移时失败就自动回滚,配置如下:
# GitLab CI的核心配置文件,放在项目根目录
stages:
- check-migration # 先做脚本顺序校验
- deploy-migrate # 再做迁移部署
# 迁移脚本顺序校验任务
flyway-script-check:
stage: check-migration
image: maven:3.8.6-openjdk-11 # 用带Java和Maven的镜像,适配Flyway命令
script:
# 下载并安装Flyway命令行工具,方便在CI里调用
- wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/8.5.13/flyway-commandline-8.5.13-linux-x64.tar.gz | tar xz && mv flyway-8.5.13 /usr/flyway
- export PATH=$PATH:/usr/flyway
# 进入迁移脚本的存放目录
- cd src/main/resources/db/migration
# 核心命令:校验所有脚本的版本顺序,顺序不对直接返回错误,CI就会终止
- flyway validate -url=jdbc:mysql://test-db:3306/test_db -user=test -password=test123 -locations=filesystem:.
only:
- develop # 只在合并到develop分支时触发,主分支可以加更严格的校验
# 迁移部署+自动回滚任务
deploy-with-rollback:
stage: deploy-migrate
image: maven:3.8.6-openjdk-11
script:
- wget -qO- https://repo1.maven.org/maven2/org/flywaydb/flyway-commandline/8.5.13/flyway-commandline-8.5.13-linux-x64.tar.gz | tar xz && mv flyway-8.5.13 /usr/flyway
- export PATH=$PATH:/usr/flyway
- cd src/main/resources/db/migration
# 先跑迁移脚本,失败就执行回滚
- flyway migrate -url=jdbc:mysql://test-db:3306/test_db -user=test -password=test123 || flyway undo -url=jdbc:mysql://test-db:3306/test_db -user=test -password=test123
only:
- develop
这里的关键是flyway validate命令,它会检查所有脚本的版本号是否递增,有没有重复的版本号,脚本的命名是否符合规则,只要有一个问题,CI就会直接终止,不让部署;flyway migrate后加||就是“如果前面的命令失败,就执行后面的回滚命令”,自动把数据库恢复到迁移前的状态。
四、方案的优缺点分析
4.1 优点
- 强制脚本顺序:Flyway的校验功能会把乱序的脚本直接拦截,从根源上解决执行顺序乱的问题;
- 自动校验+回滚:CI全程自动化,不用人工盯流水线,出问题自动回滚,降低人工故障排查的成本;
- 适配现有工具:Flyway和CI都是DevOps流水线的常用组件,不需要重构现有流程,接入成本低;
- 历史可追溯:脚本的版本号会被存在数据库的
flyway_schema_history表里,谁改了哪个版本、什么时候改的都能查,方便问题排查。
4.2 缺点
- 命名规则严格:必须按Flyway的规则命名脚本,开发者刚开始可能不习惯,需要统一规范;
- 回滚脚本需手动写:每次写迁移脚本都要对应写回滚脚本,万一漏写,迁移失败就没法自动回滚,得手动改;
- 大项目效率稍降:如果有几百个迁移脚本,CI校验和跑迁移的时间会变长,但对于中小团队来说影响不大。
五、落地时的注意事项
5.1 脚本命名绝对不能乱
必须严格遵守V版本号__描述.sql的规则,比如不能写成V2add_phone.sql(少了下划线),也不能写V1.1_add_phone_v2.sql(版本号乱加),Flyway识别不了会直接报错。
5.2 基线版本设置要对
如果项目已经有旧的数据库表,第一次用Flyway时,要把baseline-version设置成当前数据库的最大版本号,不然Flyway会从V1开始跑,重复执行旧脚本,导致数据冲突;
5.3 测试环境必须先跑
不能直接把迁移脚本放到生产环境,要先在测试环境用CI跑一遍,确保脚本能正常执行,没有数据类型或逻辑错误,再合并到主分支;
5.4 回滚脚本要同步更新
每次写新的迁移脚本,必须同步写对应的回滚脚本,比如加了字段要写删字段的脚本,改了类型要写改回原类型的脚本,不然迁移失败只能手动改,会耽误时间。
六、总结
数据库迁移脚本顺序乱导致的数据不一致,是DevOps流水线里非常常见的故障,尤其是并行开发多的团队,很容易踩这个坑。用Flyway做版本控制,配合GitLab CI做自动校验和回滚,能从流程上杜绝这个问题,而且接入成本低,不需要大改现有流程,适合大部分中小团队。只要严格遵守脚本命名规则,同步写回滚脚本,就能把数据库迁移的风险降到最低,让DevOps流水线更稳定。
Comments