Flyway 数据库版本管理完整实践指南

适配 Flyway 13.x(OSS 开源版),包含本地 Shell 脚本方案 + Maven / Gradle Java 项目集成,Mac M 系列开发踩坑总结。

一、什么是 Flyway

Flyway 是一款开源数据库版本迁移工具,把数据库变更(DDL/DML)当做代码一样管理,SQL 脚本纳入 Git 版本控制。

  • 核心:基于 SQL 脚本驱动,简单直接,不需要写 Java 代码

  • 自动维护一张 flyway_schema_history 历史表,记录每一条脚本执行状态

  • OSS 开源版没有 undo 回滚,回滚采用正向补偿脚本方案

  • 支持命令行、Maven、Gradle、SpringBoot 多种方式

⚠️开源版限制:Undo 回滚、部分高级校验为商业 Pro 功能。

二、本地 Shell 命令行方案

1. 目录结构约定

flyway-demo/
├── db
│   └── migration          #迁移脚本目录,固定
│       ├── V1__init_demo.sql
│       ├── V2__add_user_table.sql
│       └── R__view_demo.sql
├── drivers                #存放JDBC驱动jar(mysql驱动)
├── flyway.conf            #flyway配置文件
├── .env.mysql.dev         #环境模板
└── flyway‑env.sh          #环境加载别名脚本

2.SQL 脚本文件名规范

  1. 版本脚本 V 开头(最常用) V<版本>__<描述>.sql两个下划线分隔

V1__init_demo.sql
V2__add_user_table.sql
V3__alter_user_add_phone.sql
  • V 大写,版本号递增,不能重复

  • 双下划线__,单下划线识别失败

  • 已经执行成功的脚本禁止修改内容,修改会触发 checksum 校验失败

  • 需要变更,新建更高版本脚本

  1. 可重复脚本 R 开头(视图 / 存储过程 / 函数)

R__view_user_stats.sql

无版本号;文件内容发生变化就会重复执行;不要用来做建表改表

  1. Undo 脚本 Uxx__:仅 Flyway Pro 商业版可用,开源版无效。

3. 脚本内部规范

  • 文件编码:UTF‑8 无 BOM

  • 多条语句使用分号;结尾

  • 存储过程、触发器需要手动修改DELIMITER分隔符

DELIMITER //
CREATE PROCEDURE sp_demo()
BEGIN
  SELECT 1;
END //
DELIMITER ;
  • 注释支持 --单行、/* */多行

4.5 个核心命令别名详解

先加载环境:source ./flyway‑env.sh mysql dev

命令

作用

使用时机

是否修改数据库

fw‑info

查看迁移状态

每次操作前必看;确认 Pending/Success/Failed 状态

❌只读

fw‑migrate

执行所有 Pending 待执行脚本

新增脚本后执行,上线执行

✅修改库

fw‑validate

校验脚本一致性

CI 流水线、上线前校验;检查 checksum、缺失脚本

❌只读

fw‑repair

修复 flyway_schema_history 历史表

迁移失败状态 Failed;修改已执行脚本文件名后修复

✅修改历史表

fw‑baseline

打基线

老项目已有业务表接入 Flyway;不执行 SQL,只写入基线记录

✅创建历史表

✅全新空白库:直接fw‑migrate,不要 baseline。 ✅老数据库已有表:使用fw‑baseline打基线,跳过历史版本脚本。

5. 开源版如何回滚数据库

Flyway OSS 没有 undo 回滚命令,两种方案:

  1. 正向补偿脚本(推荐,行业标准) 不要修改旧 V 脚本,新建更高版本脚本抵消之前变更。 例:V2 创建 user 表,要撤销,新建V3__drop_user_table.sql

DROP TABLE IF EXISTS `user`;

执行fw‑migrate完成回滚,所有变更留痕。

  1. 备份恢复(生产故障兜底) 上线前使用 mysqldump 备份数据库,故障时恢复快照。

#备份
mysqldump -uroot -p flyway_demo > backup.sql
#恢复
mysql -uroot -p flyway_demo < backup.sql

❌禁止:手动修改 / 删除flyway_schema_history表记录;不要修改已经执行成功的 V 脚本。

6. 本地开发完整工作流

  1. db/migration/新建迁移脚本

  2. fw‑info确认脚本识别,状态 Pending

  3. fw‑migrate执行变更

  4. fw‑info确认全部 Success

  5. fw‑validate校验一致性

迁移失败排错流程:

  1. fw‑info 看到状态 Failed

  2. 修改 SQL 修复问题

  3. fw‑repair 清理失败标记

  4. fw‑migrate 重新执行

三、Java 项目集成 Flyway

Flyway 会在应用启动时自动执行 db/migration 下的 SQL 脚本;也可以通过 Maven/Gradle 插件手动执行迁移,推荐开发环境使用插件,生产环境可应用启动自动执行。

前置说明

SpringBoot 项目引入 starter 即可;普通 Java 项目引入 flyway 核心包 + maven/gradle 插件。 脚本存放目录默认:src/main/resources/db/migration/


方案 1:Maven 集成(SpringBoot / 普通 Maven 项目)

① SpringBoot 项目(最简单)

maven pom.xml 引入 starter

<!-- flyway starter -->
<dependency>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-spring-boot-starter</artifactId>
</dependency>
<!-- mysql驱动 -->
<dependency>
    <groupId>com.mysql</groupId>
    <artifactId>mysql-connector-j</artifactId>
    <scope>runtime</scope>
</dependency>

application.yml配置

spring:
  datasource:
    url: jdbc:mysql://127.0.0.1:3306/flyway_demo?useUnicode=true&characterEncoding=utf8mb4
    username: root
    password: "你的密码"
  flyway:
    enabled: true          #开启flyway,应用启动自动migrate
    baseline-on-migrate: false #全新库关闭;老库接入设置true自动baseline
    locations: classpath:db/migration
    validate-on-migrate: true

把迁移 SQL 脚本放到:src/main/resources/db/migration/ 启动 SpringBoot 应用,Flyway 自动执行未运行脚本。

② Maven 插件方式(不依赖 SpringBoot,命令行执行迁移)

pom 增加 flyway‑maven‑plugin 插件

<build>
    <plugins>
        <plugin>
            <groupId>org.flywaydb</groupId>
            <artifactId>flyway-maven-plugin</artifactId>
            <version>13.3.0</version>
            <dependencies>
                <dependency>
                    <groupId>com.mysql</groupId>
                    <artifactId>mysql-connector-j</artifactId>
                    <version>8.0.33</version>
                </dependency>
            </dependencies>
            <configuration>
                <url>jdbc:mysql://127.0.0.1:3306/flyway_demo</url>
                <user>root</user>
                <password>你的密码</password>
                <locations>classpath:db/migration</locations>
            </configuration>
        </plugin>
    </plugins>
</build>

Maven 对应命令,等价前面 shell 别名:

#查看状态 等价 fw‑info
mvn flyway:info

#执行迁移 等价 fw‑migrate
mvn flyway:migrate

#校验脚本 等价 fw‑validate
mvn flyway:validate

#修复历史表 等价 fw‑repair
mvn flyway:repair

#基线 等价 fw‑baseline
mvn flyway:baseline

⚠️不要把密码硬编码 pom;可以使用 maven 属性、环境变量传入。

方案 2:Gradle 集成

① SpringBoot Gradle 项目 build.gradle

dependencies {
    implementation 'org.flywaydb:flyway-spring-boot-starter:13.3.0'
    runtimeOnly 'com.mysql:mysql-connector-j'
}

application.yml 配置和上面完全一致,启动应用自动迁移。

② Gradle 插件(命令手动执行迁移)

build.gradle

plugins {
    id 'org.flywaydb.flyway' version '13.3.0'
}

flyway {
    url = 'jdbc:mysql://127.0.0.1:3306/flyway_demo'
    user = 'root'
    password = '你的密码'
    locations = ['classpath:db/migration']
}

dependencies {
    flywayImplementation 'com.mysql:mysql-connector-j:8.0.33'
}

gradle 命令:

#查看状态
./gradlew flywayInfo

#执行迁移
./gradlew flywayMigrate

#校验
./gradlew flywayValidate

#修复
./gradlew flywayRepair

#基线
./gradlew flywayBaseline

四、生产环境最佳实践

  1. SQL 脚本提交 Git,所有变更版本受控;禁止生产手动改库。

  2. 生产环境建议:CI 流水线执行 migrate,而不是应用启动自动迁移,避免多实例并发启动重复执行。

  3. 上线前执行 info + validate 校验通过,再执行 migrate。

  4. 上线前做好数据库备份。

  5. 老系统接入 Flyway,使用 baseline 打基线。

  6. 开源版回滚:使用正向补偿脚本,不依赖 undo。

五、Mac 开发踩坑清单(我们本次遇到的坑)

  1. brew mysql@8.0 不要用sudo brew services start,会破坏文件权限,报Bootstrap failed:5

  2. Flyway13 配置变更:flyway.driverJarDirs 不能写在 conf 配置文件,只能命令行传参

  3. MySQL dyld 库缺失是 brew 包依赖损坏,重装 mysql@8.0 即可。

  4. MySQL 服务没启动,会报 Connection refused。

  5. 脚本文件名必须双下划线V1__xxx.sql,单下划线无法识别。

  6. 已经执行成功的脚本修改内容,validate 报 checksum mismatch。

六、常见问题总结

  1. Validate failed Detected resolved migration not applied to database:本地存在 Pending 脚本,执行 migrate 即可。

  2. Unknown configuration property flyway.driverJarDirs:Flyway13 配置移除,改为命令行参数。

  3. Checksum mismatch:已经执行过的脚本被修改,禁止修改旧脚本,必要时执行 repair。

  4. baseline 什么时候用:老数据库已有业务表接入 Flyway;全新库不要 baseline。

附录:简化脚本

#!/bin/bash
set -euo pipefail

echo "====================================="
echo " Flyway 多数据库项目初始化脚本 (brew flyway13 macOS)"
echo " 支持: mysql | postgresql | clickhouse | duckdb "
echo "====================================="

# 可选数据库列表
SUPPORTED_DBS=("mysql" "postgresql" "clickhouse" "duckdb")

echo "请选择目标数据库:"
for i in "${!SUPPORTED_DBS[@]}"; do
    echo "  $((i+1)). ${SUPPORTED_DBS[$i]}"
done
read -p "输入序号选择: " DB_SELECT_IDX
DB_INDEX=$((DB_SELECT_IDX - 1))
DB_TYPE=${SUPPORTED_DBS[$DB_INDEX]}

read -p "开发环境数据库主机 [127.0.0.1]: " DB_HOST_DEV
DB_HOST_DEV=${DB_HOST_DEV:-127.0.0.1}

read -p "生产环境数据库主机: " DB_HOST_PROD

read -p "开发环境数据库端口: " DB_PORT_DEV
read -p "生产环境数据库端口: " DB_PORT_PROD

read -p "开发环境数据库名: " DB_NAME_DEV
read -p "生产环境数据库名: " DB_NAME_PROD

read -p "开发环境数据库用户: " DB_USER_DEV
read -p "生产环境数据库用户: " DB_USER_PROD

# 创建标准目录
mkdir -p db/migration
mkdir -p drivers
echo "[+] 创建目录 db/migration , drivers/"

# 生成示例迁移脚本
cat > db/migration/V1__init_demo.sql <<'EOF'
-- Flyway V1 初始化示例脚本
CREATE TABLE IF NOT EXISTS demo (
    id INT AUTO_INCREMENT PRIMARY KEY,
    remark VARCHAR(255),
    create_at DATETIME DEFAULT CURRENT_TIMESTAMP
);
EOF
echo "[+] 示例脚本 db/migration/V1__init_demo.sql"

# 根据数据库类型生成JDBC URL模板
case "${DB_TYPE}" in
mysql)
URL_DEV_TPL="jdbc:mysql://${DB_HOST_DEV}:${DB_PORT_DEV}/${DB_NAME_DEV}?useUnicode=true&characterEncoding=utf8mb4&useSSL=false"
URL_PROD_TPL="jdbc:mysql://${DB_HOST_PROD}:${DB_PORT_PROD}/${DB_NAME_PROD}?useUnicode=true&characterEncoding=utf8mb4"
;;
postgresql)
URL_DEV_TPL="jdbc:postgresql://${DB_HOST_DEV}:${DB_PORT_DEV}/${DB_NAME_DEV}"
URL_PROD_TPL="jdbc:postgresql://${DB_HOST_PROD}:${DB_PORT_PROD}/${DB_NAME_PROD}"
;;
clickhouse)
URL_DEV_TPL="jdbc:clickhouse://${DB_HOST_DEV}:${DB_PORT_DEV}/${DB_NAME_DEV}"
URL_PROD_TPL="jdbc:clickhouse://${DB_HOST_PROD}:${DB_PORT_PROD}/${DB_NAME_PROD}"
;;
duckdb)
URL_DEV_TPL="jdbc:duckdb:./${DB_NAME_DEV}.db"
URL_PROD_TPL="jdbc:duckdb:${DB_NAME_PROD}.db"
;;
*)
echo "不支持的数据库类型"
exit 1
;;
esac

ENV_DEV_TPL=".env.${DB_TYPE}.dev.template"
ENV_PROD_TPL=".env.${DB_TYPE}.prod.template"

# 生成开发环境模板
cat > "${ENV_DEV_TPL}" <<EOF
# ${DB_TYPE} DEV 环境模板
# 使用前复制: cp ${ENV_DEV_TPL} .env.${DB_TYPE}.dev
export FLYWAY_URL="${URL_DEV_TPL}"
export FLYWAY_USER="${DB_USER_DEV}"
export FLYWAY_PASSWORD=""
EOF

# 生成生产环境模板
cat > "${ENV_PROD_TPL}" <<EOF
# ${DB_TYPE} PROD 环境模板
# 使用前复制: cp ${ENV_PROD_TPL} .env.${DB_TYPE}.prod
export FLYWAY_URL="${URL_PROD_TPL}"
export FLYWAY_USER="${DB_USER_PROD}"
export FLYWAY_PASSWORD=""
EOF

echo "[+] 生成模板 ${ENV_DEV_TPL}"
echo "[+] 生成模板 ${ENV_PROD_TPL}"

# 主 flyway.conf,全部取自环境变量
cat > flyway.conf <<'EOF'
# ==================================
# Flyway 主配置文件
# 所有参数由 shell 环境变量注入
# ==================================
flyway.url=${FLYWAY_URL}
flyway.user=${FLYWAY_USER}
flyway.password=${FLYWAY_PASSWORD}

flyway.locations=filesystem:./db/migration
flyway.driverJarDirs=./drivers

flyway.baselineOnMigrate=true
flyway.validateOnMigrate=true
flyway.cleanDisabled=true
EOF
echo "[+] 生成 flyway.conf"

# 环境切换脚本 flyway‑env.sh
cat > flyway-env.sh <<'EOF'
#!/bin/bash
# 用法 source ./flyway-env.sh <dbtype> <env>
# 示例:
#    source ./flyway-env.sh mysql dev
#    source ./flyway-env.sh mysql prod

if [ $# -ne 2 ];then
  echo "用法: source ./flyway-env.sh <dbtype> <dev|prod>"
  echo "示例: source ./flyway-env.sh mysql dev"
  exit 1
fi

DBTYPE="$1"
ENV="$2"

ENV_FILE="./.env.${DBTYPE}.${ENV}"

if [ ! -f "${ENV_FILE}" ];then
  echo "❌ 环境文件不存在: ${ENV_FILE}"
  echo "💡提示:请复制模板生成真实环境文件:"
  echo "cp .env.${DBTYPE}.${ENV}.template ${ENV_FILE}"
  echo "然后编辑填入 FLYWAY_PASSWORD"
  exit 1
fi

echo "✅加载环境: db=${DBTYPE} , env=${ENV}"
set -a
source "${ENV_FILE}"
set +a

alias fw-info="flyway info"
alias fw-migrate="flyway migrate"
alias fw-validate="flyway validate"
alias fw-repair="flyway repair"
alias fw-baseline="flyway baseline -baselineVersion=1"

echo "已加载别名:"
echo "  fw-info      查看迁移状态"
echo "  fw-migrate   执行迁移"
echo "  fw-validate  校验脚本"
echo "  fw-repair    修复历史表"
echo "  fw-baseline  基线初始化"
echo "💡直接输入 fw‑migrate 即可执行迁移"
EOF

chmod +x flyway-env.sh
echo "[+] 生成 flyway‑env.sh"

# gitignore
cat > .gitignore <<'EOF'
*.log
drivers/*.jar
# 真实环境文件忽略
.env.*
# 保留模板文件允许提交git
!.env*.template
EOF
echo "[+] 生成 .gitignore"

echo ""
echo "====================================="
echo "✅项目初始化完成"
find . -maxdepth 2
echo ""
echo "📋使用步骤:"
echo "1.复制模板生成真实环境文件:"
echo "   cp ${ENV_DEV_TPL} .env.${DB_TYPE}.dev"
echo "   cp ${ENV_PROD_TPL} .env.${DB_TYPE}.prod"
echo "2.编辑 .env.${DB_TYPE}.dev / .env.${DB_TYPE}.prod,填入 FLYWAY_PASSWORD"
echo "3.加载开发环境: source ./flyway‑env.sh ${DB_TYPE} dev"
echo "4.加载生产环境: source ./flyway‑env.sh ${DB_TYPE} prod"
echo "5.执行 fw‑info 查看状态,fw‑migrate 执行迁移"
echo ""
echo "⚠️注意事项"
echo " 1. JDBC驱动jar放到 ./drivers 目录"
echo " 2. 数据库实例需要预先手动创建,flyway不会创建database"
echo " 3. .env.* 文件已加入gitignore,禁止提交到代码仓库,仅 .*.template 可以提交"
echo "====================================="


Flyway 数据库版本管理完整实践指南
https://blog.cikaros.cn/archives/flyway-shu-ju-ku-ban-ben-guan-li-wan-zheng-shi-jian-zhi-nan
作者
Cikaros
发布于
2026年08月26日
许可协议