1. 概述
Flyway迁移并不总是按计划进行,在本教程中,我们将探索从失败的迁移中恢复的选项。
2. 设置
让我们从一个基本的Flyway配置的Spring Boot项目开始,它具有flyway-core、spring-boot-starter-jdbc和flyway-maven-plugin依赖项。
更多配置细节请参考我们Flyway介绍的文章。
2.1 配置
首先,让我们添加两个不同的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 迁移
让我们添加第一个迁移文件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. 运行迁移
现在,让我们运行应用程序并尝试应用迁移。
首先是默认的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 检查状态
在继续修复数据库之前,让我们通过运行以下命令检查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 更正错误并重新运行迁移
让我们通过将表名从table_one更正为table_two来修复迁移文件V1_1__add_table.sql。
现在,让我们尝试再次运行该应用程序:
mvn spring-boot:run -Ph2
我们现在注意到H2迁移失败:
Validate failed:
Detected failed migration to version 1.1 (add table)
只要此版本的迁移已经失败,Flyway就不会重新运行1.1版本的迁移。
另一方面,postgre Profile成功运行。如前所述,由于回滚,状态很干净,可以应用更正后的迁移。
实际上,通过运行mvn flyway:info -Ppostgre我们可以看到两个迁移都成功应用。因此,总而言之,对于PostgreSQL,我们所要做的就是更正我们的迁移脚本并重新触发迁移。
4. 手动修复数据库状态
修复数据库状态的第一种方法是手动从flyway_schema_history表中删除Flyway条目。
让我们简单地对数据库运行此SQL语句:
delete from flyway_schema_history where version = '1.1';
现在,当我们再次运行mvn spring-boot:run时,我们会看到迁移已成功应用。
但是,直接操作数据库可能不是理想的选择。所以,让我们看看我们还有什么其他选择。
5. Flyway Repair
5.1 修复失败的迁移
让我们继续添加另一个损坏的迁移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 重新对齐校验和
通常建议不要更改已成功应用的迁移,但有些情况下可能别无选择。
因此,在这种情况下,让我们通过在文件开头添加注释来更改迁移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回调
如果我们不想手动干预,我们可以考虑在迁移失败后自动从flyway_schema_history中清除失败条目的方法。为此,我们可以使用afterMigrateError Flyway回调。
我们首先创建SQL回调文件db/callback/afterMigrateError__repair.sql:
DELETE FROM flyway_schema_history WHERE success=false;
每当发生迁移错误时,这将自动从Flyway状态历史记录中删除任何失败的条目。
让我们创建一个application-callbacks.properties Profile配置,它将在Flyway位置列表中包含db/callback文件夹:
spring.flyway.locations=classpath:db/migration,classpath:db/callback
现在,在添加了另一个损坏的迁移V1_3__add_table.sql之后,我们运行包括回调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. 总结
在本文中,我们研究了从失败的Flyway迁移中恢复的不同方法。
我们看到,像PostgreSQL这样的数据库(即支持DDL事务的数据库)不需要额外的努力来修复Flyway数据库状态。
另一方面,对于像H2这样没有这种支持的数据库,我们了解了如何使用Flyway Repair来清理Flyway历史记录并最终应用更正的迁移。
Post Directory
