Flyway 数据库迁移工具详解
1. 概述
Flyway 是一款把数据库 Schema 变更(Migration,迁移脚本)纳入版本控制的工具,由 Axel Fontaine 于 2010 年创建,现归 Redgate 公司所有。它将"数据库像代码一样管理"——迁移脚本用 SQL 编写、放进版本控制、按版本顺序执行,让开发、测试、生产各环境的数据库始终保持一致且可追溯,是 Database DevOps(数据库 DevOps)领域事实上的标准工具。
- 增量迁移:每个变更是一个带版本号的 SQL 脚本,只执行一次
- Schema 历史表:自动记录已应用哪些脚本、何时、何人、结果如何
- 校验机制:用 Checksum 检测已应用的脚本是否被篡改
- 一键部署:一条
flyway migrate即可把数据库迁移到最新状态
1.1 Flyway 支持什么
| 维度 | 说明 |
|---|---|
| 客户端形式 | 命令行 CLI、Java API、Maven 插件、Gradle 插件、Spring Boot 自动配置、Docker 镜像、Flyway Desktop(GUI) |
| 数据库 | PostgreSQL、MySQL/MariaDB、Oracle、SQL Server、SQLite、DB2、H2、MongoDB、Cassandra 等 30+ 种关系型与部分 NoSQL 数据库 |
| 迁移格式 | SQL 脚本(原生数据库语法)、Java 迁移类(复杂数据转换/LOB 处理)、脚本迁移(Groovy/Python 等) |
| 版本层级 | Community(社区版,核心功能免费)、Teams(团队版,含 undo/自定义校验规则等)、Enterprise(企业版,含 diff/generate/snapshot 等数据库比较与漂移检测) |
diff / generate / model / snapshot / check -drift,Teams 的 undo)。历史背景:Flyway 由 Axel Fontaine 于 2010 年创建时完全开源,2016 年被 Redgate 收购后转为 open core——核心免费开源,靠高级功能商业变现。因此"开源"不等于所有能力免费,字段级结构对比等能力恰好属于闭源的 Enterprise 版。
1.2 专有名词解释
| 英文术语 | 中文 | 含义 |
|---|---|---|
| Migration | 迁移 | 对数据库 Schema 或数据的一次增量变更,通常是一个 SQL 脚本 |
| Versioned Migration | 版本化迁移 | 带版本号、每个环境只执行一次的迁移(如 V1__create_table.sql) |
| Repeatable Migration | 可重复迁移 | 无版本号、文件内容变化(checksum 改变)时重新执行的迁移(如视图、存储过程,R__xxx.sql) |
| Undo Migration | 撤销迁移 | 用于回滚对应版本化迁移的脚本(U__xxx.sql,Teams 版) |
| Baseline | 基线 | 给一个"已经存在"的数据库打底,把当前状态标记为某个版本,跳过其下所有迁移 |
| Schema History Table | Schema 历史表 | Flyway 自动创建的特殊表,默认 flyway_schema_history,记录迁移的完整审计轨迹 |
| Checksum | 校验和 | 迁移脚本内容计算的哈希值,用于检测脚本是否被修改 |
| Placeholder | 占位符 | 脚本中的 ${xxx},执行时被替换,用于区分环境 |
| Callback | 回调 | 挂在迁移生命周期上的脚本(如 afterMigrate.sql),自动在特定时机执行 |
| Flyway Desktop | 桌面客户端 | Redgate 提供的图形化界面,用于开发人员管理迁移脚本与版本控制 |
2. 核心概念
2.1 迁移类型
Flyway 的迁移分为三种:版本化迁移(Versioned)、可重复迁移(Repeatable)、基线迁移(Baseline)。其中版本化迁移又分为普通迁移和撤销迁移(Undo)。
| 类型 | 文件前缀 | 执行时机 | 典型用途 |
|---|---|---|---|
| 版本化迁移 | V | 按版本顺序、每个环境只执行一次 | 建表/改表/删表、索引、外键、枚举、UDT;参考数据更新;用户数据修正 |
| 可重复迁移 | R | 每次 migrate 时,若 checksum 变化则重新执行 | 视图、存储过程、函数、物化视图的(重新)创建;批量参考数据重灌 |
| 撤销迁移(Teams+) | U | 通过 undo 命令手动触发 | 撤销对应版本化迁移的副作用 |
| 基线迁移 | B(自动) | 对已存在数据库执行 baseline 时记录 | 项目中途接入 Flyway,跳过历史遗留脚本 |
执行顺序规则:在单次 migrate 运行中,先按版本顺序执行所有待执行的版本化迁移,再按描述(description,字母序)执行所有已变更的可重复迁移。
CREATE OR REPLACE。
2.2 命名规范
迁移脚本的文件名由四部分构成,可被配置项调整:
[prefix][version]__[description].[suffix]
────────┬─────── ────┬─────── ─────┬─────
前缀 V/R/U 分隔符 __ 后缀 .sql
V1__create_user_table.sql ← 版本化迁移,版本 1
V2.1__add_age_column.sql ← 版本支持点号/下划线,如 V2_1 等价
R__all_views.sql ← 可重复迁移,无版本号
U1__create_user_table.sql ← 撤销迁移,对应 V1
版本号必须唯一,且按数值排序执行(V1 < V2 < V2.1 < V10)。描述仅用于可读性。用零填充(如 V001)或时间戳可减少多人协作时的冲突。
| 配置项 | 默认值 | 说明 |
|---|---|---|
sqlMigrationPrefix | V | 版本化 SQL 迁移前缀 |
sqlMigrationSeparator | __(双下划线) | 版本与描述的分隔符 |
sqlMigrationSuffixes | .sql | SQL 迁移后缀,可配多个 |
repeatableSqlMigrationPrefix | R | 可重复 SQL 迁移前缀 |
undoSqlMigrationPrefix | U | 撤销 SQL 迁移前缀 |
2.3 Schema 历史表(flyway_schema_history)
flyway_schema_history 是一张表,不是数据库。
它位于你连接的目标数据库内部(默认在 defaultSchema 中,如 PostgreSQL 的 public.flyway_schema_history)。
Flyway 本身不会创建数据库,只通过 JDBC 连接到已存在的库。
Flyway 只创建一张表——就是这张历史表。数据库里的其他表(users、orders 等应用表)都是迁移脚本执行后产生的,不是 Flyway 创建的。
数据库中各类对象及关系:
| 对象 | 创建者 | 作用 |
|---|---|---|
| 数据库(Database) | 人工 / 基础设施(如 Docker、云 RDS) | 承载所有表,Flyway 只连接、不创建 |
flyway_schema_history(1 张) | Flyway 首次 migrate 自动创建 | 记录"哪个脚本、何时、何人、结果如何"的执行轨迹(审计) |
| users / orders 等应用表(N 张) | 迁移脚本(V*.sql / R*.sql) | 业务数据存储,是脚本执行的"结果" |
三者关系(一对多、无外键):
- 历史表(1 张)↔ 迁移脚本(N 个):一对多——一张历史表记录所有已应用脚本,每行对应一个已应用的迁移(版本化 V / 可重复 R / 基线 B)
- 迁移脚本(N 个)→ 应用表(M 张):每个脚本可创建 / 修改多张应用表
- 历史表与应用表之间没有外键约束:历史表是独立的审计表,删除应用表不影响历史表,反之亦然;历史表只能由 Flyway 通过 migrate / repair / baseline 等命令写入
首次 migrate 时,Flyway 会在默认 Schema 中自动创建历史表,作为所有数据库变更的完整审计轨迹。表名可用 table 配置项修改。
| 字段 | 类型 | 含义 |
|---|---|---|
installed_rank | int | 应用顺序排名(主键) |
version | varchar | 迁移版本号(可重复迁移为空) |
description | varchar | 迁移描述 |
type | varchar | 迁移类型(SQL / JDBC / BASELINE / UNDO_SQL 等) |
script | varchar | 脚本文件名 |
checksum | int | 脚本内容校验和(用于 detect 篡改) |
installed_by | varchar | 执行迁移的数据库用户 |
installed_on | timestamp | 执行时间 |
execution_time | int | 执行耗时(毫秒) |
success | boolean | 是否成功 |
通过 flyway info 命令可以查看每条迁移在目标库上的状态(Migration States):
| 状态 | 含义 |
|---|---|
Pending | 尚未应用,等待 migrate |
Success | 执行成功 |
Failed | 执行失败 |
Missing | 历史表中有记录但本地找不到对应脚本(脚本被删) |
Future | 本地版本落后于已部署环境(脚本存在于更高版本) |
Out of Order | 乱序应用,重跑整个历史可能得到不同结果 |
Baseline | 已通过 baseline 标记为基线 |
Ignored | 已忽略,migrate 时不再考虑(repair 后产生) |
Deleted | 被 repair 标记为已删除 |
2.4 占位符(Placeholders)
所有迁移脚本都支持占位符替换,默认使用 Ant 风格 ${myplaceholder}。常用于抽象环境差异(如表名、所有者、时区等)。
-- V1__create_user_table.sql
CREATE TABLE ${table_prefix}users (
id BIGINT PRIMARY KEY,
name VARCHAR(255) NOT NULL
);
-- 配置占位符
flyway.placeholders.table_prefix=app_
flyway.placeholders.table_prefix=app_
内置占位符 ${flyway:timestamp} 每次执行都会改变,把它放在可重复迁移顶部,可以让该迁移每次都强制执行(常用于强制刷新物化视图等场景)。
2.5 回调(Callbacks)
把脚本命名为回调事件名并放入回调目录,即可在迁移生命周期的特定时点自动执行。常用于:重编译存储过程、刷新物化视图、VACUUM 等数据库维护。
| 事件 | 触发时机 |
|---|---|
beforeMigrate / afterMigrate | migrate 前 / 后 |
beforeEachMigrate / afterEachMigrate | 每个迁移脚本前 / 后 |
afterMigrateError | 迁移失败后 |
beforeValidate / afterValidate | validate 前 / 后 |
beforeClean / afterClean | clean 前 / 后 |
beforeBaseline / afterBaseline | baseline 前 / 后 |
2.6 配置方式
Flyway 支持多种配置来源,优先级从高到低:命令行参数 > Java API 编程配置 > 环境变量 > 配置文件(flyway.conf 或 application.yml)。
# flyway.conf 示例
flyway.url=jdbc:postgresql://localhost:5432/demo
flyway.user=postgres
flyway.password=secret
flyway.locations=filesystem:./sql
flyway.baselineOnMigrate=true
flyway.placeholders.env=dev
3. 行为图(系统"做什么")
3.1 用例图 — Flyway 用户交互全景
3.2 活动图 — migrate 主流程
migrate 是 Flyway 的核心命令:扫描迁移脚本 → 对比数据库已应用记录 → 补齐差距。它是幂等的,无论数据库当前处于哪个版本都可安全执行。
3.3 时序图 — migrate 全过程
一次 flyway migrate 中,Flyway 引擎、迁移脚本与目标数据库之间的交互细节:
3.4 状态机图 — 迁移状态流转
每条迁移在 flyway_schema_history 中会处于不同状态,随着 migrate / repair / baseline 等操作不断流转:
4. 结构图(系统"由什么组成")
4.1 部署图 — 多种接入方式
Flyway 是一个库 + 多种前端的工具:CLI、Java API、构建工具插件、Spring Boot 自动装配、Docker 容器等全部复用同一核心引擎,通过各自配置的 JDBC 驱动连接目标数据库。
4.2 组件图 — 核心架构
Flyway 核心引擎内部由"配置 → 扫描发现 → 解析 → 执行 → 历史记录"的责任链组成,各组件职责单一、通过接口协作:
4.3 包图 — 代码结构
Flyway 源码(Java)的主要包划分:org.flywaydb 根包下按职责分包,核心引擎与各前端分离:
4.4 类图 — 核心对象模型
核心门面 Flyway 类聚合配置与各能力接口,对外提供 migrate / info / validate 等命令入口:
5. 实战演示操作手册
5.1 用 CLI 从零搭建一个项目
以 macOS 上操作 PostgreSQL 为例,完整走一遍"建目录 → 写迁移 → migrate → 改脚本 → 再迁移"的闭环。
步骤 1:下载并安装 Flyway CLI
# 从 https://www.red-gate.com/products/flyway/ 下载最新版(Community 免费)
# 解压后加入 PATH
export PATH="$HOME/flyway-10.x/flyway:$PATH"
flyway -v
步骤 2:创建项目结构与首个迁移
mkdir -p demo-flyway/sql
cd demo-flyway
cat > sql/V1__create_user_table.sql <<'EOF'
CREATE TABLE users (
id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
name VARCHAR(255) NOT NULL,
email VARCHAR(255) UNIQUE,
created_at TIMESTAMP DEFAULT NOW()
);
EOF
步骤 3:配置并执行 migrate
cat > flyway.conf <<'EOF'
flyway.url=jdbc:postgresql://localhost:5432/demo
flyway.user=postgres
flyway.password=secret
flyway.locations=filesystem:./sql
EOF
flyway migrate
# 输出: Successfully applied 1 migration to schema "public" (execution time 00:00.123s)
此时数据库中已自动创建 public.flyway_schema_history 表。查看状态:
flyway info
# ┌──────┬─────────┬─────────────────┬─────┬────────────────────────┬────────┐
# │State │ Version │ Description │Type │ Script │ ... │
# ├──────┼─────────┼─────────────────┼─────┼────────────────────────┼────────┤
# │ ✔ │ 1 │ create_user... │ SQL │ V1__create_user_table.sql│ ... │
# └──────┴─────────┴─────────────────┴─────┴────────────────────────┴────────┘
步骤 4:追加新迁移(推进 schema)
cat > sql/V2__add_profile_column.sql <<'EOF'
ALTER TABLE users ADD COLUMN profile VARCHAR(2000);
EOF
flyway migrate # 只应用 V2
flyway info # V1 仍为 Success,V2 变 Success
步骤 5:验证与校验
# 改一下已应用的 V1(比如加个空格),再执行 validate:
flyway validate
# ✗ ERROR: Migration checksum mismatch for migration version 1
# → applied to database : X, resolved locally : Y
validate 发现 checksum 不一致。若确实要改,用 flyway repair 重新对齐历史表的 checksum:
flyway repair
flyway validate # 通过
clean 会删除配置 Schema 中的所有对象,仅用于开发/测试环境,严禁在生产环境执行。
5.2 集成 Spring Boot
Spring Boot 只需引入依赖 + 配置,应用启动时(JPA 初始化前)自动执行迁移:
<!-- pom.xml -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
<!-- 数据库驱动按需引入,如 flyway-database-postgresql / mysql-connector-j 等 -->
# application.yml
spring:
flyway:
enabled: true
locations: classpath:db/migration
baseline-on-migrate: true
datasource:
url: jdbc:postgresql://localhost:5432/demo
username: postgres
password: secret
把迁移脚本放在 src/main/resources/db/migration/(Spring Boot 默认位置):
src/main/resources/db/migration/
├── V1__create_user_table.sql
└── V2__add_profile_column.sql
启动应用,日志中即可看到 Flyway ... Successfully validated 2 migrations 与 Migrating schema 输出。Maven 构建时也可用插件手动触发:
mvn flyway:migrate # 默认绑定 pre-integration-test 阶段
gradle flywayMigrate
5.3 已有数据库如何接入 Flyway(Baseline)
项目已运行很久、数据库里已有大量对象,此时接入 Flyway 不能重跑历史脚本,用 baseline 打底:
# 假设现有 Schema 相当于"版本 1"的状态
flyway baseline -baselineVersion=1 -baselineDescription="Existing schema"
# 结果:历史表中插入一条 BASELINE 记录,版本 1,成功
flyway info
# │ Baseline │ 1 │ Existing schema │ BASELINE │ ... │ ✔ │
之后 flyway migrate 只会应用版本 > 1 的迁移(版本 ≤ 1 的脚本被视为 Below Baseline 而跳过)。也可直接开启自动基线:
flyway migrate -baselineOnMigrate=true # 历史表为空且库非空时自动 baseline 到最新版本
6. 参数指令速查手册
6.1 命令速查
| 命令 | 层级 | 说明 |
|---|---|---|
flyway migrate | Community | 把数据库迁移到最新版本(核心命令,幂等) |
flyway info | Community | 查看所有迁移及目标库上的状态(Pending/Success/...) |
flyway validate | Community | 校验本地迁移与已应用记录是否一致(checksum) |
flyway baseline | Community | 为已有数据库打基线(-baselineVersion / -baselineDescription) |
flyway repair | Community | 修复历史表:删除失败记录、重新对齐 checksum、删除已删除迁移 |
flyway clean | Community | 开发专用:删除 Schema 中所有对象(生产禁止!) |
flyway undo | Teams | 回滚最近一次已应用的版本化迁移 |
flyway check -code | Community | 对迁移做代码审查(正则规则 / SQLFluff 集成) |
flyway check -dryrun | Teams | 预演迁移会执行哪些 SQL,输出 dry-run 脚本 |
flyway check -drift | Enterprise | 检测目标库与迁移的漂移(schema drift) |
flyway add | Community | 按命名规范创建空迁移脚本模板 |
flyway diff / generate / model | Enterprise | 数据库比较、自动生成迁移脚本、维护 Schema 模型 |
flyway snapshot | Enterprise | 对数据库状态做快照,用于漂移检测与回滚 |
flyway list-engines | Community | 列出支持的数据库引擎及版本 |
flyway init | Community | 初始化新 Flyway 项目(默认配置 + 目录结构) |
mvn flyway:migrate | — | Maven 插件执行迁移(默认阶段 pre-integration-test) |
gradle flywayMigrate | — | Gradle 插件执行迁移 |
6.2 常用配置参数速查
| 参数 | 默认值 | 说明 |
|---|---|---|
flyway.url | — | 目标数据库 JDBC URL(必填,如 jdbc:postgresql://host:5432/db) |
flyway.user / password | — | 数据库连接凭据 |
flyway.locations | filesystem:./sql | 迁移脚本位置,多个用逗号分隔,递归扫描;前缀 filesystem:/classpath: |
flyway.schemas | 默认连接 Schema | Flyway 管理的 Schema 列表(会尝试创建) |
flyway.defaultSchema | schemas 第一个 | 历史表所在 Schema |
flyway.table | flyway_schema_history | 历史表名 |
flyway.createSchemas | true | 是否自动创建配置的 Schema |
flyway.baselineOnMigrate | false | 非空库且无历史表时是否自动 baseline |
flyway.baselineVersion | 1 | baseline 打底的版本号 |
flyway.target | 最新 | migrate 目标版本(如 3 只迁到 V3) |
flyway.placeholderReplacement | true | 是否启用占位符替换 |
flyway.placeholders.<name> | — | 定义占位符值,${name} 在执行时替换 |
flyway.sqlMigrationPrefix | V | 版本化迁移前缀 |
flyway.repeatableSqlMigrationPrefix | R | 可重复迁移前缀 |
flyway.sqlMigrationSeparator | __ | 版本与描述分隔符 |
flyway.callbacks | filesystem:./sql/callbacks | 回调脚本目录 |
flyway.ignoreMigrationPatterns | — | 自定义校验忽略规则(Teams+) |
6.3 常见问题排查
| 症状 | 原因 / 排查方向 |
|---|---|
Checksum mismatch | 已应用的迁移脚本被改动。确认是否有意修改:有意则 flyway repair 重新对齐;否则回退脚本改动 |
Detected resolved migration not applied to database | 本地多了脚本但库未应用,正常执行 flyway migrate 即可;若脚本被删则 repair |
No database found to handle ... | 缺少对应数据库的 flyway-database-xxx 驱动模块 |
| 迁移执行失败、库回滚 | 每个迁移默认包在单个事务中,失败自动回滚;检查 SQL 报错信息后修复脚本,再 migrate |
| 生产误跑 clean | 无法恢复!务必:禁止在生产配置 clean、用只读账号、CI 中显式禁用 |
| 多人同时提交相同版本号 | 迁移冲突。约定使用时间戳版本或 Git 预提交校验,冲突者重编号 |
Out of Order 警告 | 乱序应用过迁移。确认可接受后,配置 outOfOrder=true 忽略校验 |