一、前言

在软件开发中,数据库迁移是保障项目持续发展的关键任务。Flyway作为一款备受青睐的数据库迁移工具,为开发人员提供了高效管理数据库版本的方案。然而,在使用过程中,“Validate failed: Migration checksum mismatch for migration version”报错常常给开发者带来困扰。最常见的就是改了已执行的sql脚本,这个其实很好解决。今天就简单聊聊~~~

二、Flyway 基础

(一)Flyway 是什么

Flyway是一款开源的数据库迁移工具,支持多种主流数据库,如MySQL、PostgreSQL等。它通过版本化管理数据库迁移脚本,让开发者能够像管理代码版本一样轻松管理数据库变更,确保不同环境下数据库结构的一致性。

(二)Flyway 的工作原理

Flyway主要包含迁移脚本、元数据表和Flyway引擎三个核心组件。迁移脚本通常是SQL文件或Java代码,按照特定命名规范编写,用于定义数据库的变更操作。元数据表(默认名为flyway_schema_history)用于记录每个迁移脚本的执行信息,包括版本号、脚本名称、执行时间、校验和等。Flyway引擎负责扫描迁移脚本、计算校验和、执行迁移操作并更新元数据表。

其工作流程一般为:首次运行时检查数据库是否存在元数据表,若不存在则创建;接着扫描指定目录或类路径下的迁移脚本,按版本号排序;然后对每个脚本计算校验和并与元数据表中的记录对比;若校验通过,则执行未执行过的脚本,并将执行信息记录到元数据表中。

(三)校验和计算机制

Flyway使用Adler32算法计算迁移脚本的校验和。它会读取脚本内容,对其进行标准化处理,去除换行符、注释和多余空格,再用Adler32算法计算出唯一的校验和值。这个值会被存储在元数据表中,用于后续验证脚本是否被修改。

三、报错原因分析

(一)脚本内容修改

这是导致校验和不匹配最常见的原因。对已执行过的迁移脚本进行任何修改,哪怕只是一个字符的变动,都会使重新计算的校验和与元数据表中记录的不一致。例如,修改SQL语句中的注释、调整字段名或更改数据类型,都可能引发该问题。

(二)手动修改元数据表

直接在数据库中手动修改元数据表中的校验和或其他关键信息,会破坏数据的一致性,导致Flyway在验证时发现校验和不匹配,进而报错。

(三)环境差异

不同操作系统或开发环境中,文件的编码格式、换行符等存在差异,这些差异会影响脚本内容的字节表示,从而导致校验和计算结果不同。比如,Windows系统的换行符是\r\n,而Linux系统是\n,如果在不同系统间切换脚本,就可能出现校验和不一致的情况。

(四)工具版本差异

不同版本的Flyway对校验和的计算方式或迁移逻辑可能存在细微差别。在不同环境中使用不同版本的Flyway,可能会导致校验和不匹配问题的出现。

四、解决方案

(一)恢复迁移脚本

如果是因为误修改了迁移脚本导致校验和不匹配,可通过版本控制系统(如Git)找到脚本的历史版本,将其恢复到最初执行时的状态,从而解决问题。

(二)使用 Flyway 的repair命令

Flyway提供的repair命令可用于修复元数据表中的校验和问题。在Maven项目中,可在命令行执行mvn flyway:repair;在Gradle项目中,执行gradle flywayRepair。也可以在Java代码中使用Flyway API进行修复:

import org.flywaydb.core.Flyway;

public class FlywayRepairExample {
    public static void main(String[] args) {
        Flyway flyway = Flyway.configure()
               .dataSource("jdbc:mysql://localhost:3306/your_database", "username", "password")
               .load();
        flyway.repair();
    }
}

该命令会更新元数据表中的校验和,使其与当前脚本的校验和一致,同时删除失败的迁移记录(仅适用于不支持DDL事务的数据库)。

(三)手动更新校验和

若确定修改迁移脚本是必要的,且希望更新校验和,可手动操作。先使用类似前面介绍的Java代码计算当前脚本的校验和,然后打开数据库的元数据表,找到对应的迁移记录,将新计算的校验和更新到记录中。但需注意,手动修改数据库记录存在风险,操作前务必备份数据。

(四)忽略校验和验证

在某些特殊情况下,可选择暂时忽略校验和验证。在Spring Boot项目的application.yml文件中添加如下配置:

spring:
  flyway:
    validate-on-migrate: false

也可以在Java代码中进行配置:

import org.flywaydb.core.Flyway;

public class FlywayIgnoreValidationExample {
    public static void main(String[] args) {
        Flyway flyway = Flyway.configure()
               .dataSource("jdbc:mysql://localhost:3306/your_database", "username", "password")
               .validateOnMigrate(false)
               .load();
        flyway.migrate();
    }
}

不过,这种方法不建议长期使用,因为校验和验证是确保数据库迁移一致性的重要机制。

五、Flyway 常用命令拓展

(一)migrate命令

用于执行所有未执行的迁移脚本,将数据库升级到最新版本。在Maven项目中执行mvn flyway:migrate;在Gradle项目中执行gradle flywayMigrate。

(二)clean命令

该命令用于删除数据库中的所有对象(如表、视图、存储过程等),常用于测试环境的重置。在Maven项目中执行mvn flyway:clean;在Gradle项目中执行gradle flywayClean。

(三)info命令

执行info命令可显示数据库的迁移状态信息,包括已执行的脚本、未执行的脚本、当前版本等。在Maven项目中执行mvn flyway:info;在Gradle项目中执行gradle flywayInfo。

(四)undo命令

undo命令用于撤销上一次的迁移操作,将数据库回退到上一个版本。但该命令仅在支持撤销操作的数据库中可用。在Maven项目中执行mvn flyway:undo;在Gradle项目中执行gradle flywayUndo。

六、Flyway 配置参数详解

在Spring Boot应用中,可通过application.yml文件对Flyway进行配置,以下是一些常见参数:

spring:
  datasource:
    type: com.zaxxer.hikari.HikariDataSource
    url: jdbc:mysql://192.168.x.xx:3306/core
    username: xxx
    password: xxxxxxx
    hikari:
      poolName: Hikari
      auto-commit: false
  flyway:
    enabled: true
    schemas: public
    encoding: UTF-8
    locations: classpath:db/migration
    sql-migration-prefix: V
    sql-migration-separator: __
    sql-migration-suffixes:.sql
    table: flyway_schema_history
    baseline-on-migrate: true
    validate-on-migrate: true
    baseline-version: 2.0.0.1

(一)基础配置参数

  1. enabled:默认值为true,用于控制Flyway是否启用。设为false时,Flyway不会执行任何迁移操作。
  2. locations:默认值是classpath:db/migration,指定迁移脚本的存放位置。可以是文件系统路径或类路径,支持多个路径,用逗号分隔。
  3. schemas:由Flyway管理的schema名称(区分大小写),默认是public。可指定多个schema,同样以逗号分隔。
  4. encoding:默认编码为UTF_8,用于设置SQL迁移文件的编码格式。确保编码设置正确,避免因编码问题导致迁移失败。
  5. table:默认值为flyway_schema_history,是Flyway用于记录架构历史的表名。可自定义该表名,但需注意保持一致性。
  6. sqlMigrationPrefix:SQL迁移文件的文件名前缀,默认是V。例如,V1__Initial_Setup.sql。
  7. sqlMigrationSeparator:SQL迁移文件的文件名分隔符,默认是__。用于分隔版本号和描述信息。
  8. sqlMigrationSuffixes:SQL迁移文件的文件名后缀,默认是.sql。也支持多个后缀,如.sql,.sqlj。
  9. repeatableSqlMigrationPrefix:可重复SQL迁移文件的文件名前缀,默认是R。可重复迁移脚本会在每次脚本内容发生变化时重新执行。
  10. cleanDisabled:用于控制是否禁用数据库清理功能,默认为false(不禁用)。
  11. cleanOnValidationError:表示在验证错误时是否自动调用清理操作,默认值为false。
  12. baselineVersion:默认值为1,执行基线操作时用于标记现有模式的版本。只有在flyway_schema_history表不存在时,该参数才会生效。若指定了此参数,大于该版本号的SQL文件才会被检查和执行。
  13. target:指定应迁移到的目标版本。不配置时,Flyway会迁移到最新版本;若指定了版本号,则只迁移到该版本,后续版本不会升级。
  14. initSqls:可指定获取连接后立即执行的初始化连接SQL语句。但使用该参数建库时需注意,数据库连接参数url中需指定库名,若库未创建则会连接报错。
  15. baselineOnMigrate:控制迁移非空schema时是否自动调用基线操作。当数据库已存在,且包含数据库表和数据,但flyway_schema_history表不存在时,该参数尤为重要。若schema为空,此参数无意义,Flyway会直接走创建过程。当判断schema非空时:
    • 设置为false,且flyway_schema_history表不存在,会返回报错。
    • 设置为true,则会创建flyway_schema_history表,并按照版本号逐个执行SQL文件。
  16. validateMigrationNaming:默认值为false,控制是否验证脚本命名是否遵守正确约定的迁移和回调。设置为true时,Flyway会对脚本命名进行严格检查。
  17. validateOnMigrate:默认值为true,执行迁移时会自动调用验证操作。若已存在flyway_schema_history表,Flyway会对其中记录的每个SQL文件的checksum字段进行校验。

七、SQL编写注意事项(MySQL)

(一)建表语句

在创建表时,使用IF NOT EXISTS子句避免重复创建。例如:

CREATE TABLE IF NOT EXISTS user (
    id INT AUTO_INCREMENT PRIMARY KEY,
    username VARCHAR(50) NOT NULL,
    email VARCHAR(100) UNIQUE
);

(二)添加字段语句

添加字段时,同样使用IF NOT EXISTS判断,防止重复添加。如:

ALTER TABLE user ADD COLUMN age INT IF NOT EXISTS;

(三)删除语句

删除表、字段或约束时,使用IF EXISTS判断,避免因删除不存在的对象而报错。示例如下:

DROP TABLE IF EXISTS temp_table;
ALTER TABLE user DROP COLUMN IF EXISTS address;
ALTER TABLE user DROP FOREIGN KEY IF EXISTS fk_user_role;

(四)修改语句

修改表结构时,要谨慎操作。例如修改字段类型,需确保数据的兼容性。若要修改user表中age字段的数据类型:

ALTER TABLE user MODIFY COLUMN age TINYINT;

在执行此类操作前,建议备份数据,以防数据丢失或损坏。

Logo

一站式 AI 云服务平台

更多推荐