Flyway进行数据库版本管理

Flyway 说明

官方文档:Documentation

参考博客:Flyway客户端使用

命令行

Flyway支持命令行操作,需要下载对应系统的命令行客户端。下载地址Command-line tool

命令行格式:

flyway [options] command

支持的命令列表

migrate : Migrates the database

clean : Drops all objects in the configured schemas

info : Prints the details and status information about all the migrations

validate : Validates the applied migrations against the ones available on the classpath

baseline : Baselines an existing database, excluding all migrations up to and including baselineVersion

repair: Repairs the schema history table

命令详解:

命令一:Migrate

Migrate是指把数据库Schema迁移到最新版本,是Flyway工作流的核心功能,Flyway在Migrate时会检查Metadata(元数据)表,如果不存在会创建Metadata表,Metadata表主要用于记录版本变更历史以及Checksum之类的。

结合客户端理解

执行sql文件夹中未执行的sql,将sql版本更到最新。

命令二:Clean

Clean相对比较容易理解,即清除掉对应数据库Schema中的所有对象,包括表结构,视图,存储过程,函数以及所有的数据等都会被清除。 Clean操作在开发和测试阶段是非常有用的,它能够帮助快速有效地更新和重新生成数据库表结构,

特别注意:在生产环境上禁止使用该命令,使用不当将删除所有生产数据表!

结合客户端理解:

删除指定数据库里头的所有表。

命令三:Info

Info用于打印所有Migrations的详细和状态信息,其实也是通过Metadata表和Migrations完成的,下图很好地示意了Info打印出来的信息。 Info能够帮助快速定位当前的数据库版本,以及查看执行成功和失败的Migrations。

结合客户端理解:

输入所有flyway执行过的信息。

命令四:Validate

Validate是指验证已经Apply的Migrations是否有变更,Flyway是默认是开启验证的。 Validate原理是对比Metadata表与本地Migrations的Checksum值,如果值相同则验证通过,否则验证失败,从而可以防止对已经Apply到数据库的本地Migrations的无意修改。

结合客户端理解:

校验schema_version表的执行情况

命令五:Baseline

Baseline针对已经存在Schema结构的数据库的一种解决方案,即实现在非空数据库中新建Metadata表,并把Migrations应用到该数据库。 Baseline可以应用到特定的版本,这样在已有表结构的数据库中也可以实现添加Metadata表,从而利用Flyway进行新Migrations的管理了。

结合客户端理解:

设定schema_version表的基线执行版本。

命令六:Repair

Repair操作能够修复Metadata表,该操作在Metadata表出现错误时是非常有用的。 Repair会修复Metadata表的错误,通常有两种用途: 移除失败的Migration记录,该问题只是针对不支持DDL事务的数据库。 重新调整已经应用的Migratons的Checksums值,比如:某个Migratinon已经被应用,但本地进行了修改,又期望重新应用并调整Checksum值,不过尽量不要这样操作,否则可能造成其它环境失败。

结合客户端理解:

删除schema_version表里头失败的记录。

SQL脚本

Java项目中:

  • SQL脚本文件默认位置是项目的源文件夹下的db/migration 目录。
  • SQL脚本文件及Java代码类名必须遵循以下命名规则:V<version>[_<SEQ>][__description]。版本号的数字间以小数点.或下划线_分隔开,版本号与描述间以连续的两个下划线__分隔开。如V1_1_0__Update.sql。Java类名规约不允许存在小数点,所以Java类名中版本号的数字间只能以下划线进行分隔。

命令行模式:

  • SQL脚本默认存放在flyway安装目录下的sql目录下。

配置

以使用Flyway的命令行工具为例。安装命令行工具后,运行时Flyway会根据以下路径来寻找配置文件。

  • 安装目录/conf/flyway.conf
  • 用户目录/flyway.conf
  • 当前目录/flyway.conf
  • 直接在命令行使用 -configFile=myfile.conf来指定文件,例如:

flyway -configFiles=path/to/myAlternativeConfig.conf migrate

配置内容:

flyway.url=
flyway.driver=com.mysql.jdbc.Driver
flyway.user=
flyway.password=
#假如已经执行了版本1和版本3,如果增加了一个版本2,下面这个选项将会允许执行版本2的脚本
flyway.outOfOrder=true
# 修改SQL文件的目录,默认为安装目录下sql目录
# flyway.locations=

状态解释

执行flyway -X info 命令可以查看当前库的脚本执行状态:

+-----------+---------+----------------------------+------+---------------------+--------+
| Category  | Version | Description                | Type | Installed On        | State  |
+-----------+---------+----------------------------+------+---------------------+--------+
| Versioned | 1.0.1   | Init  Schema 0.3           | SQL  | 2018-06-11 19:05:41 | Future |
| Versioned | 1.0.2   | Alter system authority 0.3 | SQL  | 2018-06-11 19:05:41 | Future |
+-----------+---------+----------------------------+------+---------------------+--------+
  • Future : 脚本曾经执行成功,但在当前环境不存在(info命令更新状态)。
  • Missing : 脚本在当前环境不存在(migrate 命令更新状态)。
  • Success : 执行成功(migrate 命令更新状态)。
  • Failed : 执行失败(migrate命令更新状态)。
  • Pending : 还未执行(info命令更新状态)。

命令行使用

执行安装目录下的flyw.cmd即可。

注意事项

  1. 命令区分大小写: ` flyway -X Validate 执行失败 flyway -X validate` 执行成功

  2. 版本记录的是整个sql文件的内容,如果修改了已经执行过的版本的SQL文件中的任意内容(即使是加了个空格),也会导致校验不通过。

ERROR: Unexpected error
org.flywaydb.core.api.FlywayException: Validate failed: Migration checksum mismatch for migration version 0.3
-> Applied to database : -1854357789
-> Resolved locally    : 1683372801
        at org.flywaydb.core.Flyway.doValidate(Flyway.java:1037)
        at org.flywaydb.core.Flyway.access$100(Flyway.java:78)
        at org.flywaydb.core.Flyway$2.execute(Flyway.java:1007)
        at org.flywaydb.core.Flyway$2.execute(Flyway.java:1000)
        at org.flywaydb.core.Flyway.execute(Flyway.java:1238)
        at org.flywaydb.core.Flyway.validate(Flyway.java:1000)
        at org.flywaydb.commandline.Main.executeOperation(Main.java:165)
        at org.flywaydb.commandline.Main.main(Main.java:108)

  1. baseline是基于当前数据库内容做为一个基础版本,并设置版本号为1.0。如果后续版本号小于1.0的SQL文件,将不会执行。

  2. 在一个空数据库中,如果当前的SQL文件版本号小于1,那么执行migrate,将不会执行版本号小于1的SQL脚本。因此初始版本号不能小于1。

集成到Spring Boot

pom.xml 引入依赖

可以不用指定版本号,在spring-boot-dependencies指定了当前Spring Boot对应的版本号,例如Spring Boot 2.0对应版本为5.0.7。

       <!-- flyway -->
       <dependency>
          <groupId>org.flywaydb</groupId>
          <artifactId>flyway-core</artifactId>
      </dependency>  

SQL文件存放位置

src/main/resources/db/migration

Spring Boot配置

参考:Execute Flyway Database Migrations on Startup

Spring Boot集成了Flyway,如果没有特殊要求,只需要引入 flyway-core包即可。

详细配置见:Common application properties

默认在启动之后会执行flyway migrate 命令,SQL文件使用默认的位置classpath:db/migration,数据源使用的当前项目配置的数据源。因此如果使用的第三方数据源,可能会存在一些兼容性问题。例如druid就不能在数据库未初始化flyway库的情况下,启用wall(SQL注入防火墙),否则会报错并执行失败。因此可以在命令行手动执行flyway -X migrate初始化数据库。

Java 手动执行命令

flyway也可以手动执行,执行代码如下:

import org.flywaydb.core.Flyway;

...
Flyway flyway = new Flyway();
flyway.setDataSource(url, user, password);
flyway.migrate();

坑点

  • . 不支持回滚到某个版本,如果需要回退,必须先clean所有,再执行migrate恢复到指定版本。(注意clean之前先备份) flyway migrate flyway migrate -target=1.0.1.002

  • . 尽量不要在SQL文件中使用DROP 等指令去手动控制版本,让版本依赖于SQL文件执行的顺序。

  • . Spring Boot如果使用的数据源是Druid,那么Flyway未初始化的数据库,执行migrate操作会报错,原因是druid的wall(SQL注入防火墙)会将flayway创建flyway_schema_history 表的建表语句中的注释当成SQL注入风险,被拦截掉,从而导致创建flyway_schema_history 表失败。报错内容如下:

Script failed
-------------
SQL State  : null
Error Code : 0
Message    : sql injection violation, comment not allow : CREATE TABLE `expert_platform_backup`.`flyway_schema_history` (
    `installed_rank` INT NOT NULL,
    `version` VARCHAR(50),
    `description` VARCHAR(200) NOT NULL,
    `type` VARCHAR(20) NOT NULL,
    `script` VARCHAR(1000) NOT NULL,
    `checksum` INT,
    `installed_by` VARCHAR(100) NOT NULL,
    `installed_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `execution_time` INT NOT NULL,
    `success` BOOL NOT NULL,
    -- Add the primary key as part of the CREATE TABLE statement in case `innodb_force_primary_key` is enabled
    CONSTRAINT `flyway_schema_history_pk`PRIMARY KEY (`installed_rank`)
) ENGINE=InnoDB
Line       : 17
Statement  : CREATE TABLE `xxxx`.`flyway_schema_history` (
    `installed_rank` INT NOT NULL,
    `version` VARCHAR(50),
    `description` VARCHAR(200) NOT NULL,
    `type` VARCHAR(20) NOT NULL,
    `script` VARCHAR(1000) NOT NULL,
    `checksum` INT,
    `installed_by` VARCHAR(100) NOT NULL,
    `installed_on` TIMESTAMP NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `execution_time` INT NOT NULL,
    `success` BOOL NOT NULL,
    -- Add the primary key as part of the CREATE TABLE statement in case `innodb_force_primary_key` is enabled
    CONSTRAINT `flyway_schema_history_pk`PRIMARY KEY (`installed_rank`)
) ENGINE=InnoDB


  • 如果SQL中包含了类似于以下的SQL注释,在druid数据源执行中也会报错,但不影响最终结果。
/*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */;
/*!40101 SET NAMES utf8 */;

/*!40101 SET SQL_MODE=''*/;

/*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */;
/*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */;
/*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */;
/*!40101 SET SQL_MODE=@OLD_SQL_MODE */;
/*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */;
/*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */;
  • 在Mysql数据执行失败不会自动回滚,需要手动回滚。

  • Mysql SQL语句中不能有锁表和解锁表操作,会导致对flyway_schema_history 表解锁表操作失败。