数据库CI/CD选型与实战:对比Flyway、Liquibase、Atlas和Dolt

发布时间:2026/9/5 14:11:08
数据库CI/CD选型与实战:对比Flyway、Liquibase、Atlas和Dolt 应用代码的持续集成、持续交付做了那么多年为什么一提到“发布上线”大家还是会下意识紧张因为真正容易卡住的地方往往不在业务代码而在数据库表结构怎么改、历史数据怎么迁、多个环境的 schema 怎么保持一致。数据库 CI/CD 要解决的就是这件事——用对待代码的方式去管理数据库变更让每次结构变更都能经过评审、自动化测试和可控执行而不是靠人肉登服务器敲 SQL。2026 年再看这个领域Flyway、Liquibase、Atlas、Dolt 是绕不开的四款热门工具这篇文章就把我的选型思路和实操记录摊开来讲给被数据库上线流程折磨过的后端、DevOps 和数据同学做个参考。1. 为什么数据库 CI/CD 在 2026 年依然是重点话题1.1 应用发布已经自动化数据库变更却还在“手工时代”见过太多团队的消息流变成这种形状开发提交代码GitLab CI 自动跑单测、构建镜像、滚动发布一气呵成但数据库结构变更还是由某个人把 SQL 语句贴到生产环境终端里执行。运气好的时候SQL 语句提前经过 review运气不好的时候改错了字段类型线上直接报错然后一群人开始排查“这个表怎么多了一列”。这里的问题不是某个人不够细心而是流程错了。应用部署可以随时回滚到上一个版本因为代码有版本管理数据库却不一样数据库一旦被改过一次很难恢复成“上个结构版本”的样子。手工执行更是让所有变更都依赖个人记忆三天后没人说得清线上库跟测试库差在哪里。数据库 CI/CD 工具就是来戳破这个局面的。它把“数据库该长成什么样”以及“每一步怎么变成这样”都沉淀成可提交、可评审、可回放的文件。CI 阶段可以在临时库上跑一遍验证CD 阶段再把变更正式落到目标库每一步都有记录、有历史、可追踪。到了 2026 年这早已不是锦上添花而是稍微有点规模的项目都该具备的基础能力。1.2 三类技术路线对应完全不同的管理思维市面上的数据库 CI/CD 工具在底层设计上其实分三条路线理解清楚路线后面选工具才不会被官网宣传带偏。第一类是版本化迁移Migration代表是 Flyway、Liquibase。核心思路是你按顺序维护一串变更脚本比如 V1 建表V2 加字段V3 加索引工具负责记录哪些脚本已经在目标库执行过没有执行过的再按顺序补上。这个模型的好处是每一步都有清晰的“前后”关系和执行记录对得上适合绝大多数团队的直觉。第二类是声明式状态对比Declarative Schema代表是 Atlas。它不关心你分了多少步它只关心你定义的目标状态这张表应该有哪几列唯一索引应该建在哪几个字段上。工具自己去连数据库看当前状态然后算出两者之间的差异生成一个变更计划你再决定是否执行。这种方式非常适合数据库环境已经漂移或者你不想维护几百个编号脚本的场景。第三类是把数据库本身做成 Git 版本库代表是 Dolt。Dolt 不是挂在数据库外面的迁移工具它能像 Git 一样对数据库做分支、提交、合并数据和结构都纳入版本管理。在这种模型下数据库变更几乎就是“写代码”——拉分支、改表、提交、合并、推送上线流程体验非常原生化。我个人的感觉没有哪一条路线绝对高级关键看你团队对数据库变更的可控性要求以及现有基础设施能接受哪种工作方式。2. 四款热门工具逐项拆解参数、定位与适用边界2.1 Flyway轻量直接的老牌迁移工具Flyway 在数据库 CI/CD 这一话题里的地位基本等于“数据库界的 Git”。它把自己的核心逻辑做到了足够简单你的代码里有一个/migration 目录里面放着 V1__init.sql、V2__add_column.sql 这类命名规范的脚本执行 flyway migrate 时Flyway 会扫描本地脚本、连接数据库、查询记录表 flyway_schema_history把所有还没执行过的脚本按版本号顺序执行。我为团队选型时最看重的一点就是Flyway 的学习成本可以做到几乎为零。后端同学只要懂 SQL半小时就能把本地流程跑通。它支持的数据库也很广PostgreSQL、MySQL、Oracle、SQL Server 都在覆盖范围内而且官方文档里对 CI/CD 管道有明确推荐做法很容易嵌入 GitHub Actions 或 GitLab CI。Flyway 的短板在于可回滚性比较弱。它默认只关心“向前执行”如果你写了一个带破坏性的变更执行完才发现要回退通常要靠你自己写反向 SQL而 Flyway 不会主动帮你管理 rollback。另外这类严格依赖脚本编号的工具一旦多人并行修改同一批脚本非常容易在日常开发中产生 checksum 校验不一致的冲突。2.2 Liquibase流程更重但是企业级变更控制更完整Liquibase 和 Flyway 表面上是同类工具但用起来完全是两种感觉。Liquibase 的迁移文件叫 changelog它允许你用 SQL、XML、YAML、JSON 四种格式描述一次变更。比如用 YAML 定义“给 products 表新增 sku 字段”Liquibase 会把这个 operation 转化成对应数据库的 DDL具备很强的跨数据库移植能力。Liquibase 在流程控制上更复杂也更完整它可以给每个 changeSet 配置 precondition只有满足条件时才执行可以给脚本打 context 和 label 标签把不同环境的变更区分开还提供了 command 级别的 rollback配合自定义回滚 SQL让团队在出问题时有更清晰的撤退路线。这些能力在金融机构、国企项目或强管控场景里是比较核心的加分项。代价就是概念多、配置重。新成员要理解 changeSet 的 idauthor 唯一性、databasechangeloglock 锁表机制、diffChangelog 生成规则通常需要两三天上手。如果你的团队只是想让数据库变更跑进流水线Liquibase 可能有点“过度设计”。但如果你需要严格的审批和审计记录不要犹豫直接选它。2.3 Atlas以目标状态驱动真正适合未来 schema 自动化如果 Flyway 属于“盯着每一步看怎么做”Atlas 就是“只盯着目的地看怎么到”。它允许你用 HCL 或普通 SQL DDL 定义数据库期望的目标结构示例大概长这样table products { schema schema.public column id { type int null false } column name { type varchar(255) } primary_key { columns [column.id] } }实际运行时Atlas 先连一个 dev 环境或临时库算出“当前库结构”和“目标结构”的差异把 ADD COLUMN、CREATE INDEX、DROP COLUMN 这些操作整理成一个可审查的变更计划。比较温和的操作会自动执行危险的破坏性操作则可以被策略拦截也可以选择让相关人员在 CI 流程里 Review 后批准执行。Atlas 这种模型面对的是 2026 年更常见的痛点环境数量越来越多schema drift结构漂移越来越严重根本没人维护得清几十个环境各自执行到哪个迁移版本。用目标状态声明工具自动做“收敛”比手工维护一套几百行的迁移历史更省心。它非常适合已经具备代码评审文化和较强自动化能力的团队但对刚接触的新手来说要理解 dev-url、lint policy、migration directory 这些概念需要一定学习成本。2.4 Dolt把 Git 搬进数据库的激进派Dolt 是一个非常特别的选手。它不是“数据库迁移工具”它本身就是一个 MySQL 协议兼容的数据库。也就是说你可以用现成的 MySQL 客户端去连它但它支持 git add、git commit、git branch、git merge。在 Dolt 里表结构的每次修改和每一行数据的变动都会被当作一次提交记录下来完全按照 Git 对象模型存储。这对数据库 CI/CD 的影响是革命性的开发可以像处理代码一样为数据库拉分支。比如我想在测试库上加一个唯一约束我先建一个分支 add_unique_sku在分支里执行 ALTER TABLE提交并推送到远端然后像开 pull request 一样发起合并。如果与主分支冲突Dolt 会明确提示冲突发生在哪张表的哪些行相比传统迁移脚本的报错要直观得多。当然Dolt 也有它的问题。一是它必须作为新的数据库实例来运行对于“已经在生产环境跑了好几年 MySQL”的团队来说短期内不可能直接把业务流量切换到 Dolt 上更多只能把 Dolt 用于开发环境、测试环境或者作为 CI 流程中的临时数据库。二是它的生态还在发展中第三方工具、监控体系、云数据库托管支持都没有传统 MySQL 那么成熟。如果你追求极致一致性和数据可回放能力Dolt 值得在 2026 年保持关注但正式引入前一定要做足技术验证。3. 用同一个需求走一遍四款工具的落地姿势与关键细节3.1 需求定义给老表新增“唯一约束”字段多讲空洞的概念容易飘我拿一个特别常见的需求来对比四款工具的实际操作。假设业务系统里已经有一张原始的商品表 products早期建表时没有设置业务编号字段。现在产品要求增加一个商品唯一编号 sku并且这个字段不能重复。转换到数据库层面就两步给 products 表新增一个 sku 字段然后再给 sku 字段加上唯一索引。先看一下基础表的 SQLCREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL );然后我们期望最终结构是CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, sku VARCHAR(64), CONSTRAINT uk_products_sku UNIQUE (sku) );下面我会按 Flyway、Liquibase、Atlas、Dolt 四种方式分别说明变更文件怎么写以及落地到 CI/CD 管道时需要注意什么。3.2 Flyway写一个 V2 脚本就能跑用 Flyway 的话我只在 migration 目录下加一个文件命名 V2__add_sku_to_products.sql内容就是最普通的 SQLALTER TABLE products ADD COLUMN sku VARCHAR(64); ALTER TABLE products ADD CONSTRAINT uk_products_sku UNIQUE (sku);在 CI 中执行时命令可以长这样flyway -urljdbc:mysql://localhost:3306/app_db \ -userroot \ -passwordsecret \ -locationsfilesystem:./migration \ migrateFlyway 完成后会往 flyway_schema_history 表插入一条记录记录版本号、脚本描述、执行时间、脚本 checksum。下次再跑同一套迁移时它会比对文件 checksum 与库里的记录如果发现某个已执行脚本被改动过就会直接报错拒绝执行。我实际使用中的建议是不要把 V2 脚本和另一个任务里的 SQL 塞进同一个迁移文件。Flyway 的一个版本就是一个原子基线虽然部分数据库不支持跨语句事务但你至少要在审查时把它当成一个完整变更单元。多人协作时最好约定 V 后面的编号由主线分支统一分配不要每个人在分支里都写 V1.1、V1.2否则合并后很容易出现顺序冲突。3.3 Liquibase用 YAML 描述变更并主动准备回滚Liquibase 的典型 changelog 文件可以这样组织。我先写一个主文件 db/changelog/db.changelog-master.yamldatabaseChangeLog: - include: file: db/changelog/changeset/add-sku.yaml然后在 changeSet 里描述具体变更databaseChangeLog: - changeSet: id: add-sku author: zhang_san changes: - addColumn: tableName: products columns: - column: name: sku type: varchar(64) - addUniqueConstraint: tableName: products columnNames: sku constraintName: uk_products_sku rollback: - dropUniqueConstraint: tableName: products constraintName: uk_products_sku - dropColumn: tableName: products columnName: sku执行时Liquibase 会在数据库里维护两张表DATABASECHANGELOG 记录每个已执行的 changeSetDATABASECHANGELOGLOCK 用来防止多个节点同时跑 changelog 造成并发执行。CI 接入时用以下命令即可liquibase --changeLogFiledb/changelog/db.changelog-master.yaml \ --urljdbc:mysql://localhost:3306/app_db \ --usernameroot \ --passwordsecret \ update这里有个很容易踩的坑Liquibase 的 changeSet 以 idauthorfilename 三个信息作为唯一标识。如果后来你改了 id或者移动了文件路径Liquibase 会认为这是一个全新的 changeSet在目标库里再执行一遍。所以一旦某个 changeSet 被合并并执行过尽量不要改动它的标识也不要轻易修改 changes 内容。真要调整就再写一个新的 changeSet 去“订正”而不是去编辑历史。3.4 Atlas只维护最终 schema让工具自己算差异Atlas 的做法不一样我不用为这次变更单独写一个迁移脚本。我只需要确保目标 schema 文件里已经是“最终状态”。用 SQL DDL 形式来表示的话schema.sql 文件里是CREATE TABLE products ( id INT AUTO_INCREMENT PRIMARY KEY, name VARCHAR(255) NOT NULL, sku VARCHAR(64), CONSTRAINT uk_products_sku UNIQUE (sku) );连接目标库以后执行 apply 命令atlas schema apply --url mysql://root:secretlocalhost:3306/app_db \ --to file://schema.sqlAtlas 会先去读 app_db 的当前状态发现它还是旧版结构没有 sku 字段于是自动生成一条 ALTER TABLE 语句把数据库改到目标状态。为了更安全地操作我通常不会直接一行 apply 上生产而是先让 CI 生成 diffatlas schema diff --from mysql://root:secretlocalhost:3306/app_db \ --to file://schema.sql \ --dev-url docker://mysql/8/app_db生成出来的差异 SQL 可以出现在 CI 的 MR/PR 评论里让有权限的人在界面点击“批准”再由下一个 job 执行。这样既享受了声明式模型的好处又保留了对生产变更的人工把关。Atlas 在 2026 年的 CI/CD 生态里还有一个优点它提供 schema lint 能力可以在发现 DROP COLUMN、DROP TABLE 这类破坏性操作时直接拦截。对付那些只想改个索引却把整表删了的“乌龙变更”这种策略非常管用。3.5 Dolt用 Git 分支与合并来管理变更用 Dolt 进行这次变更前提是应用已经使用 Dolt 作为数据库。它的流程是我个人觉得最接近“代码开发”的。我先在 Dolt 里创建一张 products 表把它推送到远程然后开始拉分支dolt checkout -b feature/add_sku dolt sql -q ALTER TABLE products ADD COLUMN sku VARCHAR(64); dolt sql -q ALTER TABLE products ADD CONSTRAINT uk_products_sku UNIQUE (sku); dolt add . dolt commit -m add sku field with unique constraint提交完成后切回主分支把特性分支合并进来dolt checkout main dolt merge feature/add_sku如果 main 分支上已经有别的同事改了同一张表Dolt 会像 Git 一样提示出现 merge conflict。你可以用 dolt conflicts cat 查看冲突行选择保留哪一版或者手工合并。合并完成后再把 main 推送到远程数据库实例整个发布链路就完成了。从“数据库 CI/CD 工具”的角度来看Dolt 最大的价值不是直接替代迁移工具而是让“数据库也可以被代码评审”。DBA 不需要脑补一个 ALTER 在生产环境会有什么影响直接看分支 diff 就能判断。这个体验在处理数据质量问题、修 bug 或做小型实验时尤其好用。4. 选型对比与经验建议不同团队请对号入座4.1 核心参数横向对比表为了更直观地展示四款工具的差异我按自己在选型中最关心的维度整理了一张对比表维度FlywayLiquibaseAtlasDolt核心模型版本化迁移版本化迁移 细粒度控制声明式状态对比Git 化数据库变更描述方式SQL 脚本SQL/XML/YAML/JSONHCL 或 SQL DDLSQL 直接改库rollback 能力需自写反向脚本支持声明更完整通过备份/计划回滚相当于 git revert学习门槛低中高中中破坏性变更保护弱弱到中强lint/policy中diff 可见适用数据库范围很广很广主流关系型MySQL 协议环境生产环境大表 DDL需谨慎需谨慎需谨慎需评估CI/CD 友好度高高高中高这里要单独解释一下“生产环境大表 DDL”这一行。四款工具本身都只是发出 ALTER TABLE 语句真正执行快慢取决于 MySQL/PostgreSQL 对表锁、行锁、在线 DDL 的支持程度。针对超大数据量我一般会建议在迁移流水线里配合 gh-ost、pt-online-schema-change 这类在线变更工具或者让 DBA 人工介入拆分步骤而不是盲目交给迁移工具一把梭。4.2 按团队画像推荐的选型结论如果你是小团队、项目刚开始、成员后端经验偏多我建议从 Flyway 入手。原因很简单它把数据库变更压缩成了“写 SQL 编号”零配置也能跑起来。团队很快就能养成“所有结构变更进代码仓库”的习惯后面再往上加 CI 验证步骤都很顺。如果你在金融、政企或者对流程审计要求很高的项目里直接看 Liquibase。它的 changelog 体系、precondition、context、rollback 概念虽然繁琐但恰好匹配这类项目“每一步都能说清来龙去脉”的合规诉求。面对客户审计时DATABASECHANGELOG 和锁表记录就是现成的证据。如果你的团队已经受够了 schema drift——每个环境结构对不上、线上改过什么没人知道Atlas 是更加现代的选择。你不再需要逐环境去数脚本执行到哪一步只需让 Atlas 把大家“拉回到”同一个目标状态。但要提前想清楚Atlas 的迁移不涉及数据本身应用初始化时需要的数据比如字典表、配置表仍然得单独准备数据迁移方案。如果你们追求研发体验愿意接受新技术栈而且核心痛点是本地开发环境难搭、测试数据不可回放那 Dolt 值得认真试水。它能把测试库、预发布库甚至 CI 临时库全用 Git 分支管理起来开发随时 checkout 一个历史版本数据来复现 bug这种体验是其他工具给不了的。4.3 我的一套快速决策检查清面对一个具体项目时我一般只问自己几个问题第一团队的数据库变更目前是“脚本维护”还是“结构漂移”占主导如果是前者Flyway 或 Liquibase 不费力如果是后者直接考虑 Atlas。第二数据库对象的管理是否需要严格审计和回滚制度需要的场景选 Liquibase不需要的选 Flyway 即可。第三除了结构我们是否还想管理“基础数据”或者“测试数据”想认真管理就研究 Dolt否则就在迁移脚本里用 Insert 语句或单独接一个数据初始化任务。第四生产环境是否有数十亿行的大表如果有任何工具都只是辅助真正的核心是数据库本身的在线变更能力和变更时窗口的调度。这四个检查点能帮你过滤掉大部分信息噪音把选择收敛到一两个候选上。5. 高频报错与多环境实施中的避坑心得5.1 Flyway 和 Liquibase 最常见的“翻车现场”Flyway 用户提得最多的问题是 checksum mismatch某天 CI 突然红了报本地脚本 checksum 与数据库记录不一致。原因基本只有一个——某个已执行过的脚本被别人修改过哪怕只改了一个空格Flyway 也能检测出来。这种情况下不要去执行 flyway repair 了事而是要回到 Git 历史里看这个脚本为什么被改。如果确实是历史脚本写错了正确做法是新增一个 V 脚本做补偿修正而不是回头改旧脚本。Liquibase 的经典问题则是 DATABASECHANGELOGLOCK 一直显示锁住。常见诱因是有人在本地手动执行了 liquibase但命令半路被 CtrlC 终止锁没有正常释放。解决办法不是直接删锁表而是先通过 DATABASECHANGELOGLOCK 查一下锁的持有时间。如果确实是僵尸进程留下的confirmed 之后再 DELETE 对应记录否则可能把正在跑的发布任务打断。我在多个项目里见到过因为删锁导致两个发布 job 并发执行同一个 changeSet最后数据被重复变更的情况所以处理锁表一定要先确认进程状态。5.2 Atlas 与 Dolt 各自容易忽略的坑Atlas 的声明式模型看起来很智能但它有个天然风险工具只看到“当前结构”和“目标结构”却不理解你真正想做什么。比如某天 schema 文件里漏写了某个字段Atlas 就可能在下一次 apply 时生成一条 DROP COLUMN 指令。若不设置 lint policy哪怕只是文件误删一行也可能导致生产数据列被删除。所以我坚持所有 Atlas 变更都必须经过 diff 评审并且开启 destructive 操作拦截策略。Dolt 的坑更多集中在转换成本。很多团队想先用 Dolt 做测试库再让测试数据同步回原有 MySQL结果发现两个数据库的复制机制、二进制日志格式、SQL 语法边界都存在差异。Dolt 是一个独立数据库不是一个“MySQL 同步插件”。如果你只因为新鲜感就把它接入现有主从架构会在同步环节耗费大量精力。Dolt 更适合你确定要用它当主力数据库的场景或者只在纯 Dolt 环境中完整走开发、测试、发布流程。5.3 大表 DDL 与 CI 重试策略最后提醒一个比工具选型更底层的问题数据库 CI/CD 跑得再顺也不能让一个业务高峰期的 ALTER TABLE 把库锁死。常见的迁移窗口策略是先确认表数据量级再选择数据库原生在线 DDL 能力必要时拆成“先加可空字段、再回填数据、再加约束”三步走。CI 重试策略也容易让人翻车。一个迁移 job 如果因为网络抖动失败平台自动重试时Flyway 可以依靠记录跳过已经成功的部分Liquibase 也会把已执行的 changeSet 跳过但如果迁移脚本写得不是幂等操作重试仍然有风险。更稳妥的方式是对“会产生外部影响的步骤”设置 no-retry或者把“迁移执行”和“迁移验证”分成两个阶段只在验证阶段做重试。我在实际推进数据库 CI/CD 项目时最深的感触是工具能解决流程自动化的问题但不能帮你补团队规范和数据库基础知识的课。先让大家在分支里写 SQL、在 MR 里 review 变更、用临时数据库验证无损性再引入再复杂的工具都会顺手很多。如果你所在团队还没开始做这一步不妨就把 Flyway 当一个起点跑起来——等大家习惯“数据库变更也要走代码评审”之后你会发现后面无论是切 Liquibase 还是 Atlas都会顺利得多。