数据库版本控制:Flyway 入门指南

数据库 DevOps Flyway

Flyway 实战指南:中小企业如何用好数据库版本控制

一句话总结:Flyway 让数据库变更像代码一样可追踪、可协作、可回滚(逻辑上)。

在中小团队中,数据库变更常常是“人肉操作”:开发在本地改表,测试手动同步,上线前 DBA 手写 SQL。结果?环境不一致、上线失败、数据错乱……
Flyway 就是为解决这些问题而生的

本文不讲理论,只聚焦 真实场景 + 推荐做法,帮你快速落地,并重点澄清两个关键限制
文件命名规范
同一个版本号只能有一个迁移文件


一、为什么用 Flyway?——解决什么痛点?

❌ 传统方式的问题

  • 开发改了表结构,测试环境没同步 → 测试失败
  • 上线时漏执行 SQL → 应用报错
  • 多人协作改数据库 → 脚本冲突、覆盖
  • 想知道“当前数据库版本”?只能靠猜

✅ Flyway 带来的价值

  • 版本化:每个 SQL 脚本都有唯一版本号(如 V1__xxx.sql
  • 自动化:应用启动自动执行未运行的迁移
  • 一致性:所有环境(dev/test/prod)结构一致
  • 可审计flyway_schema_history 表记录所有变更

二、Flyway 文件命名规范(必须遵守!)

Flyway 严格依赖文件名来识别脚本类型和执行顺序。命名错误会导致脚本被忽略或报错。

✅ 正确格式

类型命名格式示例说明
版本化迁移V<版本号>__<描述>.sqlV1__Create_users.sql必须唯一版本号,按顺序执行
可重复迁移R__<描述>.sqlR__Create_user_view.sqlchecksum 变更时重跑(用于视图、函数)

🔑 关键细节

  • VR 必须大写
  • 版本号后是 两个下划线 __(不是 _!)
  • 描述中不能包含空格或特殊字符(建议用下划线或驼峰)
  • 文件扩展名必须是 .sql
  • 可重复迁移的脚本,一定要保证脚本幂等性,例如 create table if not exists避免报错,或者唯一索引的变种通过insert ignore避免重复插入

❌ 常见错误

v1__create.sql        → 小写 v,不识别
V1_create.sql         → 只有一个下划线,不识别
V1.1__create.sql      → 版本号含点,虽合法但不推荐(用 V1_1 更稳妥)
V1__Create Table.sql  → 描述含空格,可能出问题

三、核心限制:同一个版本号只能有一个文件!

这是 Flyway 最常被忽视但最关键的规则。

❌ 错误示例

db/migration/
├── V1__Create_table.sql
└── V1__Insert_test_data.sql ⚠️ Flyway 启动时报错!

错误信息

Found more than one migration with version 1

✅ 为什么这样设计?

  • Flyway 用版本号作为主键记录在 flyway_schema_history 表中
  • 每个版本号对应一次原子变更,不能拆成多个文件

✅ 正确做法

  • 合并逻辑:把建表和初始数据写在一个 V1__Init.sql 中(如果都适用于所有环境)
  • 或按顺序编号
    V1__Create_users_table.sql
    V2__Insert_test_users.sql   -- 即使是测试数据,也用 V2

💡 记住:版本号是全局唯一的“变更 ID”,不是“功能分组”。


四、怎么集成 Flyway?——Spring Boot 5 分钟上手

Step 1:添加依赖(Maven)

<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-core</artifactId>
</dependency>

Step 2:准备符合规范的脚本

-- ✅ 正确命名:V1__Create_users.sql
CREATE TABLE users (
    id BIGSERIAL PRIMARY KEY,
    name VARCHAR(100) NOT NULL
);

Step 3:启动应用,自动迁移!

./mvnw spring-boot:run

Flyway 自动执行 V1__Create_users.sql,并在数据库创建 flyway_schema_history 表。


五、如何配置多环境?——解决“测试数据只在 dev 执行”

场景需求

  • 所有环境:都要建 users 表(V1)
  • 仅 dev:插入测试用户
  • 仅 prod:插入管理员

✅ 推荐做法:目录隔离 + 唯一版本号

1. 目录结构

src/main/resources/db/migration/
├── common/
│   └── V1__Create_users.sql
├── dev/
│   └── V2__Insert_test_users.sql     ← 版本号 V2
└── prod/
    └── V2__Insert_admin_user.sql     ← 版本号也是 V2,但不会同时加载

⚠️ 注意:dev/V2__...prod/V2__... 不会同时出现在一个环境的迁移路径中,所以不违反“版本唯一”规则。

2. 配置文件

application-dev.yml

spring:
  flyway:
    locations: classpath:db/migration/common,classpath:db/migration/dev

application-prod.yml

spring:
  flyway:
    locations: classpath:db/migration/common,classpath:db/migration/prod

3. 启动命令

# 开发环境:执行 V1 + V2(dev)
./mvnw spring-boot:run --spring.profiles.active=dev

# 生产环境:执行 V1 + V2(prod)
java -jar app.jar --spring.profiles.active=prod

绝对禁止:在同一个 locations 路径下放两个 V2 脚本!
Flyway 会在启动时直接报错退出。


六、避坑指南:中小企业最常踩的 3 个坑

坑 1:试图用多个文件共享一个版本号

  • 错误V1__a.sql + V1__b.sql
  • 正确:合并为 V1__Init.sql,或拆成 V1 + V2

坑 2:命名不规范导致脚本被忽略

  • 错误v1_create.sql(小写 v)
  • 正确V1__Create.sql(大写 V + 双下划线)

坑 3:在 prod 环境误执行 dev 脚本

  • 错误:所有脚本放同一目录
  • 正确:用 locations 严格隔离环境

七、最佳实践总结

规则说明
命名规范V<版本号>__<描述>.sql,大写 V,双下划线
版本唯一同一迁移路径下,每个版本号只能有一个文件
环境隔离common/dev/prod 目录 + Spring Profile 控制加载
版本递增即使是环境特有脚本,也按 V1 → V2 → V3 顺序编号
禁止修改已执行的脚本内容不可更改,修正请用新版本

结语

Flyway 的威力不在于复杂功能,而在于用简单规则解决协作难题
只要牢记两点:

  1. 命名要规范V1__xxx.sql
  2. 版本号不能重复(一个 V1,一个文件)

再配合多环境目录隔离,你就能在中小企业中安全、高效地管理数据库变更。

📚 官方参考Flyway Migration Naming
💡 小技巧:执行 flyway info 查看当前数据库已应用的版本,快速定位问题。