说句实在话,“数据库 CI/CD”是那种听起来谁都会、做起来最容易翻车的领域。到 2026 年,已经很少见到完全手动跑 DDL 的团队了,大家至少知道要在流水线里加一步 migration 或 deploy 的命令。可工具真不能随便选:处理不当,上一个变更执行成功却没写记录,导致下一个分支跑到生产直接栽在历史表结构上;或者一条ALTER TABLE在十万行和十亿行的表上,表现完全不是一回事。
所以我想把这四款今年讨论最多的工具——Flyway、Liquibase、Atlas、Skeema——按实际使用逻辑重新拉出来盘一遍。“盘点”不是罗列官网能力,而是帮你判断哪一款能长期匹配你团队的发布节奏、库表规模和可用性要求。这篇文章会讲清楚四款工具各自的底层思路、接入 CI/CD 的实测方式,以及老业务团队怎么不踩坑地切过去。
1. 我为什么在 2026 年还要重新做工具盘点
1.1 应用部署早就自动化了,卡流程的永远是数据库变更
先看一个很常见的团队状态:应用在 GitLab CI 上跑完测试、构建镜像、滚动发布,总共不超过十五分钟。但到了数据库变更这一步,流程一下子退回十年前——有同事用 DBeaver 连上生产库,开个事务窗口,把一句ALTER TABLE发出去,然后在群里喊一声“我改完了”。
这种模式最可怕的不是操作不规范,而是没有任何“可审计状态”。等第二个人基于旧的表结构写代码,或者第三个环境的 schema 忘同步了,问题才会集中爆发。2026 年了,微服务可以拆、K8s 可以滚动,但很多团队面对库表变更依然靠人肉。CI/CD 工具盘点的前提,是先承认这条链路一直没真正打通。
1.2 数据库变更和应用代码有三个本质不同
很多人把数据库迁移理解成“在流水线里多执行一条命令”,这是最大的误区。应用代码和数据库结构,有三点本质差异决定了工具设计的复杂度。
第一,代码产物是可替换的,数据库状态是持续累积的。一个镜像部署坏了,重新拉上一个版本就行;一个错误的DROP COLUMN执行完,旧数据不会因为你回滚镜像就回来。第二,代码回滚是“切换版本”,数据库回滚通常不是撤销 DDL,而是“再写一段新的变更去补偿”,逻辑完全不同。第三,应用代码可以写 Mock 和单测,数据库迁移要真正跑在真实引擎上才能暴露问题,测试成本天然更高。
所以在选工具前,先得确认团队是否接受“数据库变更也是有生命周期的代码”这个理念。工具只是理念的落地载体。
1.3 看似有 CI、实际没有的几种典型迹象
我判断一个团队是不是真正把数据库纳入了 CI/CD,从来不问“你用没用 Flyway”,而是直接看几个细节:
- Git 仓库里有没有 migrations 目录,还是每个人电脑里各自留一套 SQL;
- 流水线里是不是只跑
create table if not exists,一旦面对老表就全程靠人工; - 历史环境从零搭建时,能不能用一套脚本把 dev、staging、prod 的 schema 完整重建;
- 有没有人通过 Navicat 或 DataGrip 的“同步数据库”功能,在多个环境之间直接点按钮拉平;
- 发布窗口等待的到底是不是迁移审核,而不是应用发布本身。
只要中了两条以上,说明数据库还没有真正进 CI/CD。后面所有工具选型,都建立在“先承认这个问题存在”的基础上。
2. 四款热门工具解决这个问题的底层逻辑
2.1 Flyway:靠脚本顺序和版本记录跑迁移
Flyway 是最容易被接受的一类工具,因为它的心智模型很直接:你把每个变更写成一个带版本号的 SQL 文件,比如V1__create_users.sql、V2__add_email_unique.sql,工具启动时会扫描目录里的文件,按版本号排序,再和执行历史比对,只跑那些没跑过的变更。
它的核心状态存放在一个历史表里,MySQL 下默认叫flyway_schema_history。第一次执行flyway migrate会建这张表,以后每一次成功执行的脚本都会写入一条记录,包括版本号、描述、脚本 checksum、执行时间和耗时。下次再跑,工具只需要对比“文件里的版本”和“历史表里的版本”就能算出差异。
为什么 Flyway 适合大量团队?因为它把你最熟悉的 SQL 作为唯一输入,几乎不需要额外学习配置。你可以只用一行命令接入:
flyway -url=jdbc:mysql://localhost:3306/app \ -user=app_user \ -password=secret \ -locations=filesystem:database/migrations \ migrateV1 文件内容也很朴素:
-- migrations/V1__create_users.sql CREATE TABLE users ( id BIGINT PRIMARY KEY AUTO_INCREMENT, email VARCHAR(255) NOT NULL, created_at TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP );但这套简洁模型也有限制:Flyway 只关心“脚本顺序正确”,并不理解数据库当前长什么样。如果某个历史脚本因为生产 hotfix 被改动过,checksum 对不上,flyway validate就会失败。它的哲学是“宁可阻断发布,也不能让历史脚本被静默篡改”。
2.2 Liquibase:像配置管理一样管理每个变更集
Liquibase 是另一种典型的迁移工具,但它比 Flyway 抽象得更重。它把每一次变更包装成一个changeSet,用唯一的id + author标识,并统一登记在DATABASECHANGELOG表里。
核心文件是 changelog,支持 XML、YAML、JSON 和 SQL 四种格式。以 YAML 为例,一个建表变更长这样:
databaseChangeLog: - changeSet: id: create-users author: zhang changes: - createTable: tableName: users columns: - column: name: id type: BIGINT autoIncrement: true constraints: primaryKey: true - column: name: email type: VARCHAR(255) constraints: nullable: falseLiquibase 最大的特点是不一定要求你手写 SQL,它可以跨数据库生成方言。同样一份 changelog,换一个数据库 URL,工具会自动适配。这给多数据库团队省了很多事,但代价是学习曲线明显变陡。
它还有一些 Flyway 没有的编排能力。比如preConditions可以控制“只有满足条件才执行”:
databaseChangeLog: - preConditions: - runningAs: username: app_migrator - changeSet: id: 20260101-001 author: zhang changes: - sql: sql: "ALTER TABLE users ADD COLUMN nickname VARCHAR(50)"context则能把同一套 changelog 里的一部分变更限定在特定环境执行。比如context: dev的变更只用于生成测试数据,生产环境直接跳过。这种条件执行能力在大型企业里很有用,但对大多数人来说,过于灵活的后果就是排错时得同时理解“变更内容”和“执行上下文”两层状态。
2.3 Atlas:数据库领域的 Terraform
Atlas 的思路和前面两款完全不同。Flyway 和 Liquibase 是迁移式工具,记录的是“从 A 状态到 B 状态的过程”;Atlas 是期望状态式工具,你只需要描述“最终 schema 应该长什么样”,由引擎去对比现实数据库并生成执行计划。
你可以把目标 schema 定义成一个 HCL 文件:
schema "app" { charset = "utf8mb4" } table "users" { schema = schema.app column "id" { type = bigint } column "email" { type = varchar(255) } primary_key { columns = [column.id] } }然后执行:
atlas schema apply \ --url "mysql://root:pass@localhost:3306/app" \ --to "file://schema.hcl"Atlas 会先连接目标数据库,读取当前真实结构,再和 HCL 文件做 diff,计算出需要执行的 DDL。这就是为什么很多人叫它“数据库的 Terraform”。它天然支持漂移检测、CI lint、申请计划后人工审批。
但期望状态模式也有隐性成本。新团队上手会比较顺畅,因为只需要维护一份模型文件;可一旦生产库被人手工改过、或者历史债非常深,工具算出来的 diff 可能非常激进,甚至尝试重建整张表。所以 Atlas 的落地往往伴随着严格的“计划评审”和只读账号配置,不能把它当成免维护的自动同步器。
2.4 Skeema:带着 Online DDL 基因的 MySQL 专用派
Skeema 是这四个里面最“专”的,它只服务 MySQL 和 MariaDB 生态。它采用声明式思路,但和 Atlas 不同的地方在于,仓库里保存的不是一份抽象模型,而是每一张表的完整CREATE TABLE定义。
比如一个表目录会长这样:
schema/ .skeema users.sql orders.sqlusers.sql内容就是一张标准建表语句:
CREATE TABLE `users` ( `id` bigint unsigned NOT NULL AUTO_INCREMENT, `email` varchar(255) NOT NULL, `created_at` timestamp NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8mb4;Skeema 会把目录里的文件当作期望状态,连接数据库后逐表比较。skeema diff输出差异,skeema push执行变更。
它最受 MySQL DBA 欢迎的地方在于对大表变更的谨慎态度。很多团队在使用时会给它配上 gh-ost 或 pt-online-schema-change 一类的 online DDL 工具,让ALTER TABLE以异步、轻锁的方式执行,而不是直接在生产库上长时间锁表。它默认对明显的破坏性操作比较保守,你可以通过配置显式放开。这种“默认安全、显式危险”的设计,在多人协作时很值钱。
不过这也就意味着,如果你的数据库不止 MySQL 一种,Skeema 基本帮不上忙。选它之前要想清楚:团队是不是长期押注 MySQL 技术栈。
3. 同是数据库 CI/CD 工具,核心差异其实集中在四件事
3.1 迁移式 vs 期望状态式:决定了日常怎么写脚本
前面已经提到,Flyway/Liquibase 和 Atlas/Skeema 的代表性差异,其实是两种世界观的差异。
迁移式工具要求你把“数据库怎么从旧状态走到新状态”的过程全部记录下来,每一个中间步骤都要可回放。好处是生产环境的每一步都是可预测的:V3 执行完必然到 V3 的状态。坏处是如果团队积累了很长时间的手工变更,补历史基线时非常痛苦;另外多个分支同时升版本,容易出现版本号冲突或迁移依赖错乱。
期望状态式工具只要求你维护最终模型。日常开发时不会为了加一个字段专门创建一条新迁移,而是直接改表定义文件,然后让工具对比生成增量 DDL。好处是代码评审时非常直观,reviewer 看到的是完整的表结构,而不是一堆理解上下文才能看懂的碎片化脚本。坏处是工具对现实库的假设不成立时,生成的计划可能不符合预期,需要额外的人工判断层。
我的建议是:新项目、绿地团队可以认真考虑期望状态式;但如果你有几套跑了五六年的老库,过程中还穿插了大量历史 DDL,迁移式的可预测性反而更稳。
3.2 变更粒度:影响 Code Review 和冲突解决方式
工具之间的差异还体现在变更粒度。
Flyway 以文件为最小单元,一个文件通常包含多个语句,review 时看“这个版本做了哪些事”;Liquibase 把变更拆到 changeSet 级别,一个 changeset 通常只做一个原子操作;Atlas 的粒度是整个 schema 文件,任何结构性改动都是 schema 模型的变化;Skeema 的粒度则细化到每一张表。
粒度越粗,review 人越省心,但冲突概率也越高;粒度越细,合并时越灵活,但对团队规范的要求也更高。在很多团队里,我见过用 Flyway 开发的人为了保证 review 轻松,把一个版本的日志、审计、索引全塞进一个文件,结果脚本长达几百行,出了问题根本定位不到是哪一句。这不是工具的问题,是需要先把变更规范定下来。
3.3 回滚与幂等:DDL 世界里没有完美的撤销
很多人选型时会问“这工具支不支持回滚”,我的标准回答是:要分清“事务回滚”和“业务回滚”。
DDL 在 MySQL 里很多操作是隐式提交的,一旦执行成功,再用工具去“撤销”基本不可能恢复已删除的数据。Liquibase 可以写 rollback 标签,但那是你手动定义的逆向 SQL;Flyway 社区版没有真正的 undo 机制,靠编写新的迁移修复;Atlas 和 Skeema 则倾向于“改回期望状态再 push 一次”,本质上是再生成一次反向 DDL。
真正到了生产事故场景,我更推荐“用新版迁移修复”,而不是依赖工具的自动回滚。因为自动回滚假设你和上一次执行时拥有完全一致的数据状态,这在并发写入频繁的线上几乎做不到。把数据库变更当作不可变事件日志来对待,出了错就再写一条补偿事件,是长期维护最稳的思路。
3.4 数据库生态兼容性:不同团队有完全不同答案
选择工具时还要看数据库类型覆盖。Flyway 和 Liquibase 的覆盖面最广,主流关系型数据库基本都在官方支持列表里;Atlas 也支持不少常见数据库,同时对 MySQL 兼容协议有不错的适配能力;Skeema 则划定在 MySQL/MariaDB。
另外,有些走 MySQL 协议的数据库在接入 Flyway 或 Liquibase 时,可能会出现功能识别不完全的情况。原因在于迁移工具执行前往往要做数据库类型探测,自动决定使用哪套方言生成 sql,官方没有明确认证的兼容库就可能探测失败。遇到这种情况,不能只看官网说明,一定要先在临时环境里完整跑一遍迁移,确认历史表读写和 checksum 记录都正常,再放行。
四款工具的基础能力对比如下:
| 维度 | Flyway | Liquibase | Atlas | Skeema |
|---|---|---|---|---|
| 变更思想 | 顺序脚本迁移 | changeSet 迁移 | 期望状态比对 | 表定义文件比对 |
| 主要格式 | SQL | XML/YAML/JSON/SQL | HCL/SQL | SQL |
| 数据库支持 | 多数据库 | 多数据库 | 多数据库 | 仅 MySQL/MariaDB |
| 回滚方式 | 社区版无真正 undo | 手写 rollback | 改状态重新同步 | 改文件重新 push |
| 破坏性 DDL 默认处理 | 不加限制 | 不加限制 | 可配置 lint | 默认较保守 |
| online DDL 支持 | 自己控制语句 | 自己控制语句 | 依赖执行策略 | 可配合 gh-ost/pt-osc |
| 学习曲线 | 低 | 中高 | 中 | 中 |
这张表别只看某一列,要结合你自己的“日常变更频率”和“生产库规模”去判断。
4. 把这四款工具接进 CI/CD 流水线的实测做法
4.1 流水线的三个阶段:校验、计划、执行
我经手过的数据库发布流程,无论底层用哪款工具,都建议切成三个阶段,而不是一把梭地在 CI 里直接执行迁移。
第一阶段叫校验。这个阶段不碰生产库,只检查语法、文件命名、版本号、checksum 是否被篡改、目标文件能不能在临时数据库上完整执行。第二阶段叫计划。在隔离环境或临时 schema 上执行一次完整变更,生成一份“这次发布到底要改什么”的产物,给 DBA 或团队负责人做 review。第三阶段才是执行,而且执行环境必须受保护,普通开发者不应该拥有生产库的直接写权限。
很多人把工具接进流水线时只写了最后一步,导致 CI 看起来自动化了,实际上生产库还是挂着高强度账号裸奔。
4.2 Flyway 接入 GitLab CI 的参考配置
以一个标准 GitLab CI 为例,我习惯用 Flyway 官方镜像,分 verify 和 deploy 两个 Stage。
stages: - verify - deploy verify-db-migration: stage: verify image: flyway/flyway:latest services: - mysql:8.0 variables: MYSQL_DATABASE: app MYSQL_ROOT_PASSWORD: test script: - flyway -url=jdbc:mysql://mysql:3306/app -user=root -password=test -locations=filesystem:database/migrations -validateMigrationNaming=true migrate注意,这里的migrate不是直接连生产,而是连一个临时 MySQL 服务。执行成功说明本次迁移文件可以在干净库上完整跑通。如果某条迁移依赖了上一个版本写入的数据,也能在这个阶段暴露出来。
生产环境的执行建议单独放到受保护的 Stage 里,由发布人手动确认,而不是合并分支后立刻自动执行。如果你使用 GitLab 的 protected environment,可以配置仅 main 分支、仅维护者能触发。这样既保证自动化,又留了人工闸口。
deploy-db-production: stage: deploy image: flyway/flyway:latest environment: name: production only: - main when: manual script: - flyway -url=jdbc:mysql://prod-host:3306/app -user=${PROD_DB_USER} -password=${PROD_DB_PASSWORD} -locations=filesystem:database/migrations migrate-validateMigrationNaming=true是我强烈建议加的,它能让文件命名不规范、版本重复这些问题在早期就暴露。
4.3 Liquibase 的 contexts 和 preConditions 在流水线中的应用
Liquibase 接入流水线时,要重点理解 contexts 和 preConditions。
比如仓库里有一批 changeSet 只用于生成本地演示数据,你可以给它们标记context: demo,然后在 dev 环境执行时带上这个 context,在 staging 和 prod 不执行。它不改变“变更脚本是否已经执行过”这个事实,只是控制执行范围。
liquibase \ --url=jdbc:mysql://staging-host:3306/app \ --username=app_user \ --password=secret \ --changeLogFile=database/changelog.yml \ --contexts=!demo \ update!demo表示排除 demo 上下文。我相信很多人第一次用 Liquibase 时会被这种“反向过滤”搞晕,但它确实能让你一套 changelog 管理多个环境场景。
preConditions 则更适合做“安全断言”。比如某条变更只应该针对 2026 年后的表结构生效,如果生产库实际结构不符合预期,可以直接让变更失败而不是盲目执行。
4.4 Atlas 和 Skeema 在 CI 里的两个典型注意点
Atlas 因为自带 plan 和 lint 思路,接入 CI 时有天然优势。我常用的是先在 CI 里生成 plan,然后让计划产物成为流水线的一个 artifact,人工 review。核心命令大致类似这样:
atlas schema lint \ --dev-url "docker://mysql/8/dev" \ --url "mysql://ci-user:pass@localhost:3306/app" \ --exclude "atlas_schema_replica"lint 阶段可以自动拦截一些高危操作,比如删除列、修改主键类型等。团队可以把这些规则打开,让日常开发在提交代码阶段就收到反馈,而不是等 DBA 人工审核时才揪出来。
Skeema 接入 CI 的常见问题是 exit code 语义。skeema diff在有差异时返回非 0,这和很多工具“有差异但成功”的语义不一样。第一次接入的团队常常会困惑:为什么 CI 红了,数据库好像也没坏。其实这正是你想要的:它说明本地声明文件和生产库已经不一致,必须有人去处理,而不是让流水线继续往下走。我见过有人为了“让 CI 变绿”,把 diff 的 exit code 强制忽略,结果生产库漂移越来越远,这是非常危险的做法。
4.5 在 CI 里执行数据库迁移最容易踩的五个坑
第一个坑:多个 CI Job 并行跑迁移,同写一个库。应用部署可以横向扩容,数据库迁移不行。发布数据库变更的流程必须做成队列或闸口,单写者执行。
第二个坑:把生产库密码存在 CI 变量里,然后让所有开发者的分支都有权限触发。正确做法是生产发布单独环境、生产账号只授权给迁移用,甚至通过堡垒机动态下发短期凭证。
第三个坑:忘了检查乱序迁移。Flyway 默认不允许 outOfOrder,除非显式打开。有时候一个 hotfix 分支先合入生产,再合回主分支,就可能导致版本乱序。出现这种情况要理解工具为什么拒绝执行,而不是暴力清历史表。
第四个坑:大表 DDL 没有 online DDL 策略。CI 里的迁移在五六分钟超时后失败,可能不是因为语句写错,而是表行数已经大到ALTER TABLE在执行期间锁表太久,连接超时。生产的大表变更要提前想好是用 gh-ost、pt-osc,还是直接改期望状态方案配合平台在线变更。
第五个坑:DDL 执行成功但历史记录写入失败。Flyway 在部分数据库上不是把 DDL 和 history 表写入放在同一个事务里,如果网络闪断,可能出现“表已经改了但迁移记录没写进去”的情况。这种状态下重试可能报对象已存在,乱清记录只会更糟。正确做法是先人工核对当前 schema 实际状态,再决定把历史记录补上、还是把变更脚本调整成幂等的形式。这是每个数据库发布负责人迟早会遇到的场景,提前和团队约定好处理流程非常重要。
5. 老业务团队切换到这套流程,具体怎么走比较稳
5.1 老库如何接一个“干净”的基线
新项目从头接 Flyway 或 Liquibase 很简单,但存量老库才是大多数团队的真实处境。生产库里已经有一堆对象,是这些年人工执行留下的历史,你不能让迁移工具从零开始再跑一遍,否则一定会遇到“表已存在”的失败。
以 Flyway 为例,正确的基线步骤是先把当前生产库的真实 schema 记录为一个基线版本。假设你已经有一个V1__init_schema.sql对应着当前状态,那么执行:
flyway \ -url=jdbc:mysql://prod-host:3306/app \ -user=app_user \ -password=secret \ -locations=filesystem:database/migrations \ -baselineVersion=1 \ -baselineDescription="baseline from existing prod" \ baseline这个操作会在历史表里插入一条版本1的记录,但不会真的执行V1__init_schema.sql。之后 Flyway 从V2开始执行,生产库已经存在的对象不会被重复创建。
接完基线后,还有一个必须做的验证:在临时库里从零执行所有迁移文件,确认可以重建出与生产一致的 schema。这个验证很多人会偷懒跳过,但它是“新环境能不能可复现”的唯一保障。如果临时库重建后和真实生产库差异很大,说明基线并没有覆盖全部对象,需要补。
Liquibase 的逻辑类似,可以用它的 diff 能力生成一个 changelog 标记当前状态,然后通过changelogSync让数据库认为自己已经执行到当前版本,从下一个变更开始纳入管理。
5.2 多分支并发开发时,迁移文件怎么合流
小团队一个人维护 migration 目录没问题,但五六个人同时开发,每个人都可能新建V5__xxx.sql,合并时谁先谁后全靠人工,非常容易出现依赖错乱。
在 Flyway 语境里比较实用的约定是:每个 PR 只允许递增一个版本号,代码评审时看到重复版本直接拒绝合入;合入顺序严格以 Git 提交顺序为准,生产发布前在 CI 全量跑一遍迁移,确保最新主干可以从零重建。
另一个技巧是利用 ephemeral environment。环境规格可以砍,但数据库迁移必须每次都从零执行一遍,才能发现某个新脚本依赖了另一个还没合入的分支里的表。这种做法不是浪费资源,而是把问题拦在 merge 之前。
5.3 迁移脚本不进代码评审会有什么后果
我见过一个团队,他们把“数据库能连上”当成成功标准,迁移脚本完全绕过代码评审,DBA 只在生产出事时才介入。结果有一次开发为了删一个废弃字段,直接把prod当测试库,执行了ALTER TABLE orders DROP COLUMN legacy_note。表结构是改了,但下游报表里还引用着这个字段,整整一个下午业务报错。
让迁移脚本进评审,不是走流程,而是在人脑层面确认三件事:这条变更对存量数据的影响是什么;下游代码有没有同步更新;如果执行失败,补偿方案是什么。Flyway 和 Liquibase 的版本文件、Liquibase 的 changeset 都是天然适合拿来 review 的产物,Skeema 和 Atlas 的 diff 计划同样可以作为 CI artifact 供人查看。关键在于,不要把“工具能跑通”当成“变更可以上生产”的全部。
6. 我的选型参考:什么样的团队优先选哪一款
6.1 选型前先问自己四个问题
第一个问题,团队会一直围绕 MySQL 技术栈吗?如果回答是“未来多数据库可能性很大”,Skeema 就要先排除,Flyway 或 Liquibase 更稳。第二个问题,你希望团队维护“迁移过程”还是维护“最终模型”?希望开发者只管写 SQL,Flyway 上手最快;希望像管理 IaC 一样管理 schema,Atlas 会更合口味。第三个问题,生产库是不是已经有大表?大表多、变更频繁,Skeema 的 online DDL 协同能力值得倾斜考虑。第四个问题,团队有没有专职 DBA 或者严格的变更审批角色?没有专职 DBA,就更需要 Atlas/Skeema 这类能生成直观 diff 并且支持高危操作拦截的工具。
6.2 一张速查表快速定位
| 团队实际情况 | 我更推荐 | 理由 |
|---|---|---|
| 中小团队、全栈为主、希望只写 SQL 且快速落地 | Flyway | 心智模型最简单,几乎不改变已知工作方式 |
| 大型规范团队、多数据库并存、需要条件执行和复杂编排 | Liquibase | changeSet 和 context 等机制更成熟,多格式支持 |
| 以 GitOps 方式管理基础设施、希望 schema 像代码一样可评审 | Atlas | 期望状态模型天然适配 IaC,lint 能力强 |
| MySQL 专项团队、生产库表量大、非常关注 online DDL | Skeema | 与表定义文件的管理方式贴合,破坏性操作更可控 |
表格只是一个起点,真正的决定因素是你对自己团队的“变更频率”和“可容忍停摆时间”的判断。
6.3 如果团队完全没接触过数据库 CI/CD,第一步先做什么
我的建议永远是别一上来就铺四款工具对比选型。第一步只做一件事:把当前的迁移脚本放进 Git 仓库,哪怕是手写的一堆*.sql文件,先让它们有版本、有历史、有评审。然后选一个最顺手的工具,Flyway 通常是最低摩擦的选择,在 staging 环境完整跑通从零构建。等团队适应了“数据库变更也是发布的一部分”这个流程,再去考虑要不要切换到声明式、要不要接入更强的高危操作 lint。
工具选型不是一劳永逸的决策。以后如果业务从单一 MySQL 演进到多数据库,或者从手工运维演进到平台化管理,工作流迁移是可以分阶段做的。但前提是每一步都有完整的变更记录和可回放能力,否则无论换哪款工具,都只是在给历史债换一个包装。
最后说一点个人体会:我在实际维护数据库发布链路时,最看重的其实不是哪款工具生成 SQL 的能力强,而是它能不能帮你“拦住笨蛋操作”。所谓的高危 lint、默认安全策略、计划评审,本质上都是用流程成本换生产稳定性。先把流程搭对,再挑工具不迟;流程还是靠人肉,工具再热门也很难救你。