选Flyway还是Liquibase取决于是否需要跨数据库变更逻辑抽象:Flyway SQL优先、轻量可控;Liquibase DSL优先、支持多库语义抽象,changeSet可减少兼容性问题。

Flyway 和 Liquibase 到底该选哪个
选 Flyway 还是 Liquibase,取决于你是否需要「跨数据库的变更逻辑抽象」。Flyway 是“SQL 优先”,Liquibase 是“DSL 优先”(支持 XML/YAML/JSON + SQL)。如果你团队习惯写原生 ALTER TABLE、用 PostgreSQL 或 MySQL 居多、追求简单可控,Flyway 更轻量;如果项目要兼容 H2(测试)、Oracle、SQL Server 且 DBA 希望统一审核变更语义(比如“加非空列”在不同库实现方式不同),Liquibase 的 changeSet 抽象能少踩坑。
常见错误现象:FlywayMigrationException: Found non-empty schema `test` without metadata table —— 这不是报错,是 Flyway 发现库已存在表但没 flyway_schema_history 表,它不敢贸然接管。别删表硬来,得用 flyway repair 或手动初始化元数据表。
Spring Boot 中启用 Flyway 的最小配置
加依赖后几乎零配置就能跑,但容易忽略两个关键点:路径和 baseline。
-
spring.flyway.locations=classpath:db/migration必须显式指定,否则默认只扫db/migration(注意没有 classpath: 前缀时 Spring Boot 会自动补,但一旦写了就必须全对) - 已有生产库?必须设
spring.flyway.baseline-on-migrate=true,否则启动直接失败;同时配spring.flyway.baseline-version=1.0,让 Flyway 把当前库状态标记为 V1.0,后续迁移从 V1.1 开始 - 别把
V1__init.sql放进src/main/resources/db/migration后还手动执行一遍——重复执行会触发校验失败:Validation failed for migration V1__init.sql
示例文件名规范:V2__add_user_status_column.sql(双下划线分隔版本+描述,用字母 V 开头,不能用 v 或 V2.0)
Liquibase 的 changeLog 文件怎么组织才不乱
很多人一上来就堆 databaseChangeLog 在一个大 XML 里,结果 merge 冲突、回滚难、定位慢。真实协作中推荐按模块拆 + 使用 include。
- 主文件
master.xml只做 include:<include file="user/changelog.xml"/>、<include file="order/changelog.xml"/> - 每个子文件用
logicalFilePath固定标识:<databaseChangeLog xmlns="http://www.liquibase.org/xml/ns/dbchangelog" logicalFilePath="user/changelog.xml">,否则同一 changeSet 被多个文件引入时 Liquibase 会认为是重复 - 别用
runAlways="true"来绕过执行记录——它会让rollback失效,且无法被liquibase status跟踪
Java 配置里指定主文件:spring.liquibase.change-log=classpath:db/changelog/master.xml
上线前必须验证的三件事
本地跑通 ≠ 上线安全。数据库升级是不可逆操作,以下检查漏一项都可能锁表数小时。
- 确认目标库字符集与迁移脚本一致:比如 MySQL 用
utf8mb4,但脚本里写CHARSET=utf8就可能建表失败或索引截断 - 检查长事务影响:
ALTER TABLE ... ADD COLUMN在 MySQL 5.7+ 默认会重建表,千万级数据务必在低峰期执行;Liquibase 的addColumn如果没加afterColumn,某些驱动会生成不带位置的语句,导致字段顺序失控 - 验证回滚路径:Flyway 不支持自动回滚,靠人工写
UNDO_V2__add_user_status_column.sql并开启flyway.undo-sql-migration-prefix=U;Liquibase 的rollbackCount 1看似方便,但前提是所有changeSet都定义了rollback子节点,否则报No inverse to createTable
最常被跳过的动作:在预发库用真实数据量跑一次完整 migrate + rollback 流程。不是看“有没有报错”,而是看“耗时是否可接受”“锁表现是否符合预期”。










