1、概览 {#1概览}
Flyway 迁移并不总是一帆风顺行,本文将带你了解迁移失败后的恢复方案。
2、设置 {#2设置}
从基本的 Spring Boot 配置 Flyway 开始。它依赖 flyway-core
、spring-boot-starter-jdbc
和 flyway-maven-plugin
。
对于如何在 Spring Boot 中使用 Flyway 进行数据库迁移,你可以参阅 这篇文章。
2.1、配置 {#21配置}
首先,添加两个不同的 Profile(配置文件)。这能够轻松地针对不同的数据库引擎运行迁移:
<profile>
<id>h2</id>
<activation>
<activeByDefault>true</activeByDefault>
</activation>
<dependencies>
<dependency>
<groupId>com.h2database</groupId>
<artifactId>h2</artifactId>
</dependency>
</dependencies>
</profile>
<profile>
<id>postgre</id>
<dependencies>
<dependency>
<groupId>org.postgresql</groupId>
<artifactId>postgresql</artifactId>
</dependency>
</dependencies>
</profile>
还要为每个 Profile 添加 Flyway 数据库配置文件。
首先,创建 application-h2.properties
:
flyway.url=jdbc:h2:file:./testdb;DB_CLOSE_ON_EXIT=FALSE;AUTO_RECONNECT=TRUE;MODE=MySQL;DATABASE_TO_UPPER=false;
flyway.user=testuser
flyway.password=password
然后,创建 PostgreSQL application-postgre.properties
:
flyway.url=jdbc:postgresql://127.0.0.1:5431/testdb
flyway.user=testuser
flyway.password=password
注:你可以调整 PostgreSQL 配置,使其与你的数据库相匹配,也可以使用 代码示例中的 docker-compose
文件。
2.2、迁移 {#22迁移}
添加第一个迁移(migration)文件 V1_0__add_table.sql
:
create table table_one (
id numeric primary key
);
添加第二个包含错误的迁移文件 V1_1__add_table.sql
:
create table table_one (
id numeric primary key
);
这里故意犯了一个错误,使用了相同的表名。这会导致 Flyway 迁移错误。
3、运行迁移 {#3运行迁移}
现在,运行应用并尝试进行迁移。
首先是默认的 h2
Profile:
mvn spring-boot:run
然后是 postgre
Profile:
mvn spring-boot:run -Ppostgre
不出所料,第一次迁移成功,而第二次迁移失败:
Migration V1_1__add_table.sql failed
...
Message : Table "TABLE_ONE" already exists; SQL statement:
3.1、检查状态 {#31检查状态}
在进行修复之前,通过运行以下命令检查 Flyway 迁移状态:
mvn flyway:info -Ph2
返回的结果如期:
+-----------+---------+-------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+-------------+------+---------------------+---------+
| Versioned | 1.0 | add table | SQL | 2020-07-17 12:57:35 | Success |
| Versioned | 1.1 | add table | SQL | 2020-07-17 12:57:35 | Failed |
+-----------+---------+-------------+------+---------------------+---------+
但是当使用以下命令检查 PostgreSQL 的状态时:
mvn flyway:info -Ppostgre
可以看到,第二次迁移的状态是 "Pending",而不是 "Failed":
+-----------+---------+-------------+------+---------------------+---------+
| Category | Version | Description | Type | Installed On | State |
+-----------+---------+-------------+------+---------------------+---------+
| Versioned | 1.0 | add table | SQL | 2020-07-17 12:57:48 | Success |
| Versioned | 1.1 | add table | SQL | | Pending |
+-----------+---------+-------------+------+---------------------+---------+
不同之处在于,PostgreSQL 支持 DDL 事务,而 H2 或 MySQL 等其他系统则不支持。因此,PostgreSQL 可以回滚迁移失败的事务。
接下来,让我们看看当尝试修复数据库时,这种差异会产生什么影响。
3.2、纠正错误并重新运行迁移 {#32纠正错误并重新运行迁移}
修复迁移文件 V1_1__add_table.sql
,将表名从 table_one
更正为 table_two
。
们再次尝试运行应用:
mvn spring-boot:run -Ph2
H2 迁移还是失败,原因如下:
Validate failed:
Detected failed migration to version 1.1 (add table)
只要 version 1.1
的迁移已经失败,Flyway 就不会重新运行该版本的迁移。
而 postgre
Profile 运行成功。如前所述,由于进行了回滚,状态是干净的,可以应用修正后的迁移。
事实上,通过运行 mvn flyway:info -Ppostgre
你可以看到两次迁移都成功了。因此,对于 PostgreSQL 来说,所要做的就是修改迁移脚本并重新触发迁移。
4、手动修复数据库状态 {#4手动修复数据库状态}
修复数据库状态的第一种方法是从 flyway_schema_history
表中手动删除 Flyway 条目。
在数据库运行如下 SQL 语句:
delete from flyway_schema_history where version = '1.1';
现在,再次运行 mvn spring-boot:run
,就会看到迁移已成功应用。
不过,直接操作数据库可能并不理想,人工操作难免会出纰漏。
5、Flyway Repair {#5flyway-repair}
5.1、修复失败的迁移 {#51修复失败的迁移}
继续添加另一个损坏的迁移 V1_2__add_table.sql
文件,运行应用,回到迁移失败的状态。
另一种修复数据库状态的方法是使用 flyway:repair
工具。修正 SQL 文件后,可以运行以下命令,而不是手动修改 flyway_schema_history
表:
mvn flyway:repair
结果如下:
Successfully repaired schema history table "PUBLIC"."flyway_schema_history"
在底层,Flyway 只是从 flyway_schema_history
表中删除迁移失败的条目。
现在,再次运行 flyway:info
,可以看到上次迁移的状态已从 "Failed" 变为 "Pending"。
再次运行应用。可以看到,现在已经成功应用了修正后的迁移。
5.2、重新调整校验和(Checksum) {#52重新调整校验和checksum}
通常建议不要更改已成功应用的迁移。但在某些情况下可能无法避免更改迁移。
修改迁移文件 V1_1__add_table.sql
,在文件开头添加注释。
现在运行应序,会看到类似 "Migration checksum mismatch" 的错误信息:
Migration checksum mismatch for migration version 1.1
-> Applied to database : 314944264
-> Resolved locally : 1304013179
出现这种情况是因为我们更改了已应用的迁移,而 Flyway 检测到了不一致。
为了重新调整校验和,可以使用相同的 flyway:repair
命令。不过,这次不会执行迁移。只有 flyway_schema_history
表中版本 1.1
条目的校验和会被更新,以反映更新后的迁移文件。
修复后再次运行应用,会发现应用已成功启动。
注意,在本例中,我们通过 Maven 使用了 flyway:repair
。另一种方法是安装 Flyway 命令行工具并运行 flyway repair
命令。效果是一样的:flyway repair
会删除 flyway_schema_history
表中失败的迁移,并重新调整已应用迁移的校验和。
6、Flyway 回调 {#6flyway-回调}
如果不想手动干预,可以考虑在迁移失败后自动清除 flyway_schema_history
中的失败条目。为此,可以使用 afterMigrateError
Flyway Callback(回调)。
首先创建 SQL 回调文件 db/callback/afterMigrateError__repair.sql
:
DELETE FROM flyway_schema_history WHERE success=false;
只要发生迁移错误,这将自动从 Flyway 状态历史记录中删除任何失败的条目。
创建 application-callbacks.properties
配置文件,在 Flyway locations 列表中包含 db/callback
文件夹:
spring.flyway.locations=classpath:db/migration,classpath:db/callback
现在,添加另一个被破坏的迁移文件 V1_3__add_table.sql
。然后,运行应用,包括 callbacks
Profile:
mvn spring-boot:run -Dspring-boot.run.profiles=h2,callbacks
...
Migrating schema "PUBLIC" to version 1.3 - add table
Migration of schema "PUBLIC" to version 1.3 - add table failed!
...
Executing SQL callback: afterMigrateError - repair
不出所料,迁移失败了,但 afterMigrateError
回调运行并清理了 flyway_schema_history
。
只需更正 V1_3__add_table.sql
迁移文件并再次运行应用,就可以应用更正后的迁移。
7、总结 {#7总结}
本文介绍了从失败的 Flyway 迁移中恢复的不同方法。像 PostgreSQL 这样的数据库(即支持 DDL 事务的数据库)无需额外工作即可修复 Flyway 数据库状态,其他数据库则需要手动删除 Flyway 历史记录或者是使用 Flyway Repair 工具来自动清理。
Ref:https://www.baeldung.com/spring-boot-flyway-repair