侧边栏壁纸
  • 累计撰写 75 篇文章
  • 累计创建 22 个标签
  • 累计收到 1 条评论

目 录CONTENT

文章目录

Spring Boot Flyway 迁移失败问题排查与修复

七月流火
2026-05-11 / 0 评论 / 0 点赞 / 5 阅读 / 0 字

问题现象

启动 Spring Boot 项目时,控制台报错:

org.springframework.beans.factory.BeanCreationException: 
Error creating bean with name 'flywayInitializer': Validate failed: Migrations have failed validation

Detected failed migration to version 2 (add image url).
Please remove any half-completed changes then run repair to fix the schema history.

紧接着又出现:

Schema `stockkeeper` contains a failed migration to version 2!

应用无法启动,反复报同样的错误。


问题原因分析

1. 什么是 Flyway?

Flyway 是一个数据库版本管理工具,通过 SQL 脚本(称为"迁移"Migration)来管理数据库的结构变更。每个迁移脚本都有版本号,Flyway 会在数据库中维护一张 flyway_schema_history 表来记录哪些迁移已经执行过。

2. 为什么会失败?

本次问题的根因是 V2 迁移脚本执行失败

项目中有一个迁移脚本 V2__add_image_url.sql

ALTER TABLE items ADD COLUMN image_url VARCHAR(500) AFTER note;

这个脚本在某次执行时失败了(可能的原因包括:网络中断、数据库锁超时、连接断开等),导致 Flyway 在 flyway_schema_history 表中记录了一条 success = 0(失败)的记录。

3. 为什么后续启动也失败?

Flyway 有一个校验机制validate-on-migrate,默认为 true):每次启动时,它会检查 flyway_schema_history 表中是否有失败的迁移记录。如果发现任何失败的记录,Flyway 会拒绝执行新的迁移,并要求你先修复。

这就造成了一个"死循环":

  • 启动 → Flyway 发现失败记录 → 拒绝启动

  • 你无法通过正常启动来修复它


解决方案

方案一:使用 mvn flyway:repair(推荐)

Flyway 提供了一个 repair 命令,专门用于修复 flyway_schema_history 表中的失败记录。

步骤 1:创建修复脚本

由于 Windows PowerShell 对特殊字符(&@ 等)的转义处理很麻烦,建议创建一个 .cmd 批处理文件:

创建 flyway-repair.cmd

@echo off
cd /d "%~dp0"
mvn flyway:repair ^
  -Dflyway.url="jdbc:mysql://localhost:3306/your_db?useUnicode=true&characterEncoding=utf8&useSSL=false&serverTimezone=Asia/Shanghai&allowPublicKeyRetrieval=true" ^
  -Dflyway.user=root ^
  "-Dflyway.password=YourPassword"

注意:将数据库连接信息替换为你的实际配置。

步骤 2:执行修复

cmd /c flyway-repair.cmd

成功输出示例:

[INFO] Successfully repaired schema history table `stockkeeper`.`flyway_schema_history`
[INFO] Manual cleanup of the remaining effects of the failed migration may still be required.
[INFO] BUILD SUCCESS

步骤 3:重启应用

修复完成后,重启 Spring Boot 应用即可。


方案二:手动操作数据库

如果你有数据库的直接访问权限,可以手动修复。

步骤 1:查看失败记录

SELECT * FROM flyway_schema_history WHERE success = 0;

步骤 2:删除失败记录

DELETE FROM flyway_schema_history WHERE version = '2' AND success = 0;

步骤 3:检查目标表状态

确认迁移脚本的操作是否部分执行了:

SHOW COLUMNS FROM items LIKE 'image_url';
  • 有结果:说明 ALTER TABLE 已执行成功,但 Flyway 记录了失败。删除记录后重启即可。

  • 无结果:说明 ALTER TABLE 未执行。删除记录后重启,Flyway 会重新执行 V2 迁移。

步骤 4:重启应用


方案三:配置 repair-on-migrate

application.yml 中临时添加配置:

spring:
  flyway:
    repair-on-migrate: true
    validate-on-migrate: false

⚠️ 注意validate-on-migrate: false 是必须的,因为 Flyway 的执行顺序是先 validate 再 repair。如果 validate 失败,repair 根本不会执行。

启动成功后,务必恢复配置

spring:
  flyway:
    repair-on-migrate: false
    # validate-on-migrate 默认为 true,可以不写

预防措施

1. 迁移脚本要幂等

尽量让迁移脚本可以安全地重复执行。例如:

-- 不推荐
ALTER TABLE items ADD COLUMN image_url VARCHAR(500);

-- 推荐(MySQL 8.0+)
SET @column_exists = (
    SELECT COUNT(*) FROM information_schema.columns 
    WHERE table_schema = 'your_db' 
    AND table_name = 'items' 
    AND column_name = 'image_url'
);
SET @sql = IF(@column_exists = 0, 
    'ALTER TABLE items ADD COLUMN image_url VARCHAR(500)', 
    'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

2. 迁移前备份数据库

在执行重大迁移之前,先备份数据库:

mysqldump -h host -u root -p your_db > backup_before_migration.sql

3. 使用事务包装迁移

对于支持 DDL 事务的数据库(如 PostgreSQL),在迁移脚本中使用事务。MySQL 的 DDL 操作会隐式提交事务,所以这一点对 MySQL 效果有限。


实战案例:手动迁移数据库后的 Flyway 问题

背景

本项目遇到了一个更复杂的情况:数据库是从旧服务器手动迁移过来的(只迁移了数据表结构),没有携带 flyway_schema_history 表。这意味着:

  • 数据库中已有 V1 和 V2 的表结构(包括 image_url 列)

  • 但 Flyway 认为这些迁移从未执行过

  • Flyway 尝试重新执行 V2 的 ALTER TABLE items ADD COLUMN image_url,导致 Duplicate column name 错误

解决步骤

第 1 步:在 application.yml 中设置 baseline

spring:
  flyway:
    enabled: true
    baseline-on-migrate: true
    baseline-version: 2    # 告诉 Flyway:版本 2 及以下的迁移已经存在,跳过
    repair-on-migrate: false
    locations: classpath:db/migration

baseline-version: 2 表示 Flyway 会在 flyway_schema_history 中插入一条 baseline 记录(版本 2),后续只执行版本 3 及以上的迁移。

第 2 步:让迁移脚本幂等(安全可重复执行)

修改 V2__add_image_url.sql,先检查列是否已存在:

-- 为 items 表添加 image_url 字段(幂等处理:如果列已存在则跳过)
SET @column_exists = (
    SELECT COUNT(*) FROM information_schema.columns 
    WHERE table_schema = DATABASE() 
    AND table_name = 'items' 
    AND column_name = 'image_url'
);
SET @sql = IF(@column_exists = 0, 
    'ALTER TABLE items ADD COLUMN image_url VARCHAR(500) AFTER note', 
    'SELECT 1');
PREPARE stmt FROM @sql;
EXECUTE stmt;
DEALLOCATE PREPARE stmt;

第 3 步:执行 Flyway Repair 并启动

# 修复 flyway_schema_history 表
mvn flyway:repair -Dflyway.url="jdbc:mysql://..." -Dflyway.user=root -Dflyway.password="..."

# 启动应用
mvn spring-boot:run

成功输出

Successfully validated 3 migrations
Successfully applied 1 migration to schema `stockkeeper`, now at version v2
Started StockKeeperApplication in 29.141 seconds

总结

问题

原因

解决方案

Migrations have failed validation

Flyway 检测到 flyway_schema_history 中有失败记录

使用 flyway:repair 或手动删除失败记录

Schema contains a failed migration

迁移脚本执行中断,留下了半完成的状态

先修复数据库状态,再修复 Flyway 记录

配置 repair-on-migrate 不生效

validate 在 repair 之前执行,validate 失败会阻断 repair

必须同时设置 validate-on-migrate: false

Duplicate column name

手动迁移数据库后,表已存在但 Flyway 不知道

设置 baseline-version + 让迁移脚本幂等

一句话总结:手动迁移数据库后,用 baseline-version 告诉 Flyway 跳过已有版本,并让迁移脚本幂等以防止重复执行报错。

0

评论区