必须用 liquibase 或 flyway 等迁移工具而非手动 sql 改表结构,因其提供唯一 id 标识、执行记录追踪、跨环境一致性保障及冲突预防能力。

为什么不能直接用 SQL 脚本手动改表结构
因为不同环境(dev/staging/prod)的数据库状态天然不同,手动执行 ALTER TABLE 容易漏步骤、顺序错、回滚难。更麻烦的是,多人并行开发时,A 在本地加了 created_at 字段,B 在 staging 已经跑了另一版迁移,直接同步 SQL 会冲突或报错 Column 'xxx' already exists。
Liquibase 和 Flyway 的核心价值不是“多一个工具”,而是给每次结构变更打上唯一 ID + 执行记录,让数据库像代码一样可追溯、可重放、可验证。
- 必须用统一入口执行迁移,禁止绕过工具直连 prod 执行 DDL
- 迁移脚本一旦提交到 Git,就不能修改内容(可新增补偿脚本,但不可删/改已发布的
changelog或V1__xxx.sql) - Flyway 默认只认
V开头的版本化脚本;Liquibase 依赖databasechangelog表里的id+author+filename三元组去判重
Flyway vs Liquibase:选哪个取决于你团队的 SQL 熟练度
Flyway 更轻、更快,适合 DBA 或后端工程师主导、以 SQL 为主的团队。它不抽象语法,所有变更就是纯 .sql 文件,执行快、调试直观,但不支持跨数据库语法自动转换。
Liquibase 用 XML/YAML/JSON/Java 写变更逻辑,自带数据库抽象层,同一份 addColumn 声明能生成 MySQL/PostgreSQL/Oracle 对应的 SQL。适合多数据库并存、或希望把 schema 变更纳入 Java 构建流程的场景。
- 如果 CI 流水线里已有 Maven/Gradle,且想在单元测试中启动 H2 内存库跑迁移,Liquibase 的
liquibase-maven-plugin集成更顺 - Flyway 的
flyway repair能修复损坏的flyway_schema_history表,但仅限于标记为pending的脚本;Liquibase 没等价命令,出问题得手动清理databasechangelog表 - Flyway 社区版不支持 undo 迁移(需订阅 Pro),Liquibase 开源版就支持
rollback,但前提是每个变更都显式写了rollback块
如何让 dev/staging/prod 的 migration 状态始终对齐
关键不是“怎么跑”,而是“谁来触发”和“在哪校验”。别指望开发自己记着去 staging 跑一遍新脚本——必须接入部署流程。
推荐做法:应用启动时自动检查并执行 pending 迁移(Liquibase 的 spring.liquibase.enabled=true,Flyway 的 spring.flyway.enabled=true)。但生产环境必须关掉自动执行,改用独立运维命令。
- dev 环境:Spring Boot 启动时自动 run pending,方便本地调试
- staging 环境:CI 流水线在部署前执行
flyway migrate或liquibase update,失败则阻断发布 - prod 环境:由 DBA 手动执行
flyway -env=prod migrate,且必须先用flyway info确认待执行列表,再备份库后操作 - 所有环境的
flyway.locations或liquibase.change-log必须指向同一份 Git 仓库路径,比如classpath:db/migration,避免分支差异导致迁移跳步
常见翻车点:timestamp 默认值、索引名冲突、enum 变更
这些看着小,但在跨环境迁移时最常引发失败。MySQL 5.6 和 8.0 对 TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP 的语法容忍度不同;Flyway 不校验索引名是否重复,但 MySQL 会报 ERROR 1061 (42000): Duplicate key name 'idx_user_email';Liquibase 的 modifyDataType 对 enum 扩容不安全,可能锁表。
- 统一用
TIMESTAMP DEFAULT CURRENT_TIMESTAMP,去掉ON UPDATE,业务层控制更新时间 - 所有索引命名加前缀,比如
idx_order_status_created,避免不同脚本建同名索引 - enum 字段变更不要用
modifyDataType,改用addColumnType新增字段 + 脚本迁移数据 + 删除旧字段 - 上线前在 staging 用
flyway validate或liquibase checkChangeLog校验脚本合法性,它能发现语法错误、重复 ID、缺失 rollback 等问题
最复杂的不是工具配置,而是让所有人遵守“迁移即契约”的意识:一次提交 = 一次不可逆的数据库状态承诺。脚本写错可以补,但线上跳过某次迁移或手改表结构,后续所有环境对齐都会变赌运气。











