Flyway基础简介

1. 概述

Flyway是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。
自动升级(自动发现更新项):Flyway 会将任意版本的数据库升级到最新版本。Flyway 可以脱离JVM 环境通过命令行执行,可以通过Ant 脚本执行,通过Maven 脚本执行(这样就可以在集成环境自动执行),并且可以在应用中执行(比如在应用启动时执行)。
规约优于配置:Flyway 有一套默认的规约,所以不需要修改任何配置就可以正常使用。
既支持SQL 脚本,又支持Java 代码:可以使用SQL 脚本执行数据库更新,也可以使用Java 代码来进行一些高级数据升级操作。
高可靠性:在集群环境下进行数据库升级是安全可靠的。
支持清除已存在的库表结构:Flyway 可以清除已存在的库表结构,可以从零开始搭建您的库表结构,并管理您的数据库版本升级工作。
支持失败修复。新的2.0 版本提供了repair 功能,用于解决数据库更新操作失败问题。

2. flyway基础

2.1概述

  • flyway通过在数据库中维护一张版本记录表(默认名称为flyway_schema_history),对数据库脚本操作进行版本管理,使得数据库的升级更新可以自动化;
  • flyway执行的脚本支持两种形式:一是使用SQL编写的.sql脚本,二是把SQL操作写到java代码里,通过执行java代码来执行SQL;
  • 每个.sql或.class文件都是一个版本,文件名称需要遵循flyway约定的格式;这个名称中包含了一个数字的版本信息,flyway通过这个版本来进行管理;

2.2 flyway组成

  • flyway包由三部分组成
    • commandline: 对输入的命令进行处理,确定配置文件路径,查找依赖包等;
    • core: 实现flyway核心的操作(migrate/info/clean等)和版本管理
    • maven-plugin和gradle-plugin: 用于帮助打可运行的jar包;
  • flyway由java语言编写,可以直接使用core来进行开发,而不需要使用commandline
    • 比如:自己建个springboot启动工程(web或application),在里面实例化Flyway对象,然后执行对应的操作
    • 比如:自己写个main方法,在里面实例化Flyway对象,然后执行对应的操作

2.3 flyway操作

命令 说明
migrate 执行最新版本的SQL脚本和java代码,把结果反映到数据库上
clean 清除数据库中的所有表与数据,只剩下一个空的数据库
info 列出用于维护版本信息的表内容,表名默认为flyway_schema_history
validate 用实际的版本与数据库中已有的版本进行对比,检测意外修改(文件有checksum)
baseline 设定基线版本,对于已存在数据的数据,必须设置基线版本,后面从该版本之后的版本进行操作
repair 把版本表中失败的记录状态变成pending,然后可以重新操作
undo 回滚对应版本,只有商用版本才支持

2.4 版本

  • Java文件
    在这里插入图片描述
  • SQL文件
    在这里插入图片描述
  • SQL文件与Java文件的不同之处仅在于后缀不同,一个是.sql(可配置),另外一个是.class(不可配置),其它是相同的;
  • 文件名(不含后缀)的格式
    • Prefix: 前缀,可以通过配置文件来设置
      • V: 代表正常版本管理;
      • U: 表示回滚,该操作比较危险,需要做详尽测试才能用,实践中最好不用;
      • R: 表示可以重复执行的版本,比如建视图、存储过程、函数等;
    • Version: 版本号,必须由数字(可加下划线或点)组成,重复执行的脚本不需要版本;
    • Separator: 分隔版本与description的分隔符,为两个下划线,可通过配置文件来修改,默认为两个下划线;
    • Description: 文件内容描述,建议用下划线分隔单词;
    • Suffix: 只针对于sql文件(java文件不适用),可通过配置文件来设置,默认为.sql;
  • Version
    • 一般情况下使用1~N的数字即可;
    • 考虑到实际开发中可能有多个分支进行操作,为了使得分支合并不冲突,可使用时间戳来做版本(中间可用下划线或点来分隔),时间戳越精确、冲突可能性越小;

2.5 路径

  • INSTALLDIR: 安装路径,即flyway解压所在的路径,其用于:
    • 加载${INSTALLDIR}/drivers下的各种数据库driver对应的jar包;
    • 加载${INSTALLDIR}/conf下的全局配置文件;
  • workingDirectory: 工作目录,可通过指定命令参数“-workingDirectory=xxxx”(不含双引号,xxx换成具体的绝对路径),如果不指定则等同INSTALLDIR;
    • 加载${workingDirectory}/jars里的jar包,通过java编写的代码需要打成jar放到该目录下才能通过命令行的方式访问到;
    • 加载${workingDirectory}/sql里的SQL脚本文件,这个目录会被配置文件flyway.conf里的flyway.locations的值替换掉(注意是替换掉,不是并集,这个地方不好理解,原理是这两个目录都是赋值给location变量,配置文件里有设置时会覆盖掉);
    • 在flyway.conf里的flyway.locations值,如果以filesystem:开头、且是相对路径的,都会基于此路径;
  • 注意:该部分在flyway官网文档中无说明(需要查看源码才能知晓),也不好理解,容易出错,需要好好理解(有比较多的地方并不遵循后面覆盖前面的逻辑,所以不好理解)。这些路径是在commandline中实现的,也就是说只有使用commandline才用到

2.6 配置

2.6.1 配置加载

配置主要来源于flyway.conf配置文件,按以下顺序和路径进行加载配置:

  1. 先加载${INSTALLDIR}/conf/flyway.conf(flyway.conf文件名不可更改);
  2. 加载System.property中配置user.home对应的路径下的flyway.conf文件(flyway.conf文件名不可更改),加载的配置若有相同则会覆盖之前加载的;
    • System properties可以通过下面方式增加
      • java启动时使用-D加的参数,如java -Dxxx=xxxx;
      • 在java代码中使用System.setProperty(“xxx”, “xxxx”)添加的参数;
  3. 加载当前目录下的flyway.conf文件(flyway.conf文件名不可更改),加载的配置若有相同则会覆盖之前加载的;
    • 在命令行中,terminal或cmd进入的路径为当前路径
    • 在java工程中运行则是工程根目录
    • 该操作有点隐蔽,注意理解;
  4. 若在环境变量中配置了flyway.configFiles,会加载这些文件,加载的配置若有相同则会覆盖之前加载的;
    • 该配置值可以有多个文件,用逗号分隔,每个文件可以有相对路径(不能使用绝对路径),文件名也不一定是flyway.conf;
    • 这些配置文件会基于workingDirectory{workingDirectory}来构成完成路径,也就是这些文件需要放到{workingDirectory}目录下;
  5. 若命令行参数中带-configFiles参数指定了配置文件,则会加载这些文件,加载的配置若有相同则会覆盖之前加载的;
    • 若环境变量中配置了flyway.configFiles,则此配置参数无效;
    • 该配置值可以有多个文件,用逗号分隔,每个文件可以有相对路径(不能使用绝对路径),文件名也不一定是flyway.conf;
    • 这些配置文件会基于workingDirectory{workingDirectory}来构成完成路径,也就是这些文件需要放到{workingDirectory}目录下;
  6. System.in输入的配置(貌似用于测试,待研究);
  7. 用逗号分隔flyway.locations的值,如果里面的路径不是绝对路径,则用${workingDirectory}补齐;
  8. 命令行还可以指定-flyway.xxxx=xxx形式的配置,这些配置会覆盖上面已载的配置项
    • 注意:此处配置项,不是加载配置文件;

注意:

  • 该部分在flyway官网文档中无说明(需要查看源码才能知晓),也不好理解,因为配置文件名大多是固定不可变的,而可以改变的手段有点特殊。
  • 要改变flyway.conf配置文件名称,只有两种方式;
  • 该部分主要属于commandline范畴,直接使用core则可以自行构造配置项或自行指定配置文件并加载;
  • 配置里的路径可以使用变量(原理待研究);
  • 在命令行中指定-workingDirectory=xxxx时,若也指定-configFiles=flyway.conf,则会比较明显地知道配置文件是在workingDirectory目录下,但如果不指定configFiles则需要flyway.conf放到命令行进入的目录中,会有点隐蔽;
  • 配置文件里参数读取后,{}格式的变量都会被替换,变量只能来源于环境变量,不在环境变量里的变量会与{}整体被替换为空(来源于命令行的参数没有此处理过程,故不能使用${}格式的变量);

2.6.2 常用配置

# 配置sql和java code查找路径:
#    sql文件路径必须以filesystem:(含冒号)开头,可以使用变量${workdir};
#    java code路径需要以classpath:(含冒号)开头,java代码需要打成jar包,并放到工作目录里的jars目录下,jar里的目录与该配置中classpath:后配置的路径一致;
# 开发环境不要修改此值,它会自动找工程目录下的db/sql中的sql脚本,以及代码目录com/everhomes/dbmigrate中的java代码(两种都会递归找子目录)
flyway.locations=filesystem:${workdir}/sql,classpath:com/everhomes/dbmigrate

# 配置数据库连接,注意在url中不要配置用户名和密码
flyway.url=jdbc:mysql://127.0.0.1:13306/blogs?characterEncoding=UTF-8&serverTimezone=UTC
flyway.user=root
flyway.password=123456

# 当需要低版本的脚本时,需要置成true,否则只能执行高版本的脚本
flyway.outOfOrder=true

# 当数据库不是空数据库时,需要配置为true,空数据库则可以不配置
# flyway.baselineOnMigrate=true
# 配置了该版本后,migrate会从比该版本大的版本进行migrate,不配置时默认值为1
# flyway.baselineVersion=1

# 有数据的数据库请保留此值为true,禁用clean命令,执行clean命令会清空数据库
flyway.cleanDisabled=true

# 记录migrate历史记录的表名,默认为 flyway_schema_history
flyway.table=schema_version_history

# migrate前后需要做某些操作时可使用此配置项,值为类名(带包路径),多个类名之间使用逗号分隔
# 每个类需要实现org.flywaydb.core.api.callback.Callback接口
# flyway.callbacks=com.everhomes.dbmigrate.core.MigrateCallBack

2.7 版本管理

支持SQL-base和Java-base两种管理方式,两种方式产生的版本文件是共同管理的,即在命令行或Java代码工程里都有可以同时执行.sql和.class提供的版本管理,执行顺序按版本号顺序;

2.7.1 SQL-base migrate

基于SQL的版本管理:

  • 文件名称需要符合版本格式
  • 文件可以被找到
    • SQL文件是通过flyway.locations配置项中指定以filesystem:开头的路径,可以有多个、用逗号分隔;
    • flyway.locations可以通过配置文件或命令行参数指定(参考上面“配置加载”章节说明);
    • 注意:flyway.locations值虽然可以通过多个途径配置,但只能配置一个值(值中可逗号分隔),注意加载的顺序和覆盖情况;
  • 确定上面两点后,即可执行flyway migrate/info/clean/repair 来进行版本管理;
    • 命令参数中可以按规范指定参数来帮助找到flyway.locations

2.7.2 Java-base migrate

基于Java的版本管理:

  • jar形式
    • 把开发的java代码打包jar包,放到${workingDirectory}/jars中
      • java代码须继承org.flywaydb.core.api.migration.BaseJavaMigration(推荐)或实现org.flywaydb.core.api.migration.JavaMigration接口,使用此方式仅依赖flyway和jdk提供的jar即可,配置环境简单;
      • java代码尽量使用flyway和jdk提供的类和自定义的类来进行操作,否则要解决包依赖问题;
    • java代码路径可被找到(在jar包里的路径)
      • java代码如果是放到db/migration路径下(可有子目录),则不用配置其它路径即可访问,因为flyway里默认给location加了个目录: classpath:db/migration
      • 如果不是放到db/migration路径下,则需要在flyway.locations里配置以classpath:开头的路径,该路径要与jar包里的路径一致;
  • java工程形式(在IDE里运行java工程的形式)
    • 工程启动运行有两种方式
      • 引flyway的commandline包
        • 可指定flyway的安装目录,或者自行依赖数据库driver包和flyway的core包;
        • 指定workingDirectory目录;
        • 指定flyway.conf的目录,或按规则放到commandline可找到的目录;
        • 运行后执行commandline提供的org.flywaydb.commandline.Main.main(args)
        • 优点:可利用commandline已经实现的命令操作,按命令的方式操作;
        • 缺点:需要遵循commandline的方式;
      • 实例化flyway并运行
        • 按springboot的方式启动或自行写main方法启动
        • 把flyway的core引入到工程中
        • 实例化flyway,并指定数据库连接,然后调用migrate()/clean()等方法
        • 优点:灵活;
        • 缺点:需要对flyway各命令所依赖的配置项有比较多了解,才好操作;
    • 该方式不受限于db/migration目录,可随意定目录,但为了部署时可以打包jar包,推荐遵循jar包所要求的目录方式;
  • 注意:
    • 该部署在官方文档说明不详细,比较模糊,特别是jar的方式就没有说明,部署时也只能使用jar方式;
    • 官方文档主要说明了“实例化flyway”方式,但其背后依赖的配置项和规则也没有说明;

2.7.3 callback

  • 版本管理操作有生命周期管理:整个操作开始前->单个版本操作开始前->单个statement操作开始前-> 具体操作 -> 单个statement操作后(报错后)-> 单个版本操作后(错误后)-> 整个操作后(错误后)
    • 整个操作可能包含多个版本,每个版本里可能有多个sql statement
    • 操作是指migrate、clean、repair等,参考上面操作说明
  • 这些生命周期节点可以设置callback来进行一些额外的处理
    • 在java代码中,需要实现org.flywaydb.core.api.callback.Callback接口,类路径无规定
      • 接口要实现supports()方法,在里面指定哪些操作归该Callback进行处理
      • 实现handle()方法,具体处理业务
      • canHandleInTransaction()在有事务时才涉及;
    • 在flyway.callbacks配置中指定callback的类路径(带包名,多个类之间使用逗号分隔),flyway.callbacks可以在flyway.conf中指定或者在命令行参数中指定(参考上面配置加载章节说明);
    • callback的类包需要包含在jar中,才能在命令行里操作;

2.7.4 不支持MySQL5.6

  • 在org.flywaydb.core.internal.database.mysql.MySQLDatabase.ensureSupported()中有对MySQL数据库的版本进行判断,小于5.7的版本不支持(MARIADB是MySQL的一个分支,都在MySQLDatabase中实现)。
  • 除此之外,没有其它地方控制这个版本。抛出的错误说enterprise版本支持,但除非换代码,否则感觉也不支持。
  • 要支持5.6,可以修改此类编译与.class,然后替换掉core包里的该类。
public final void ensureSupported() {
    ensureDatabaseIsRecentEnough("5.1");
    if (databaseType == DatabaseType.MARIADB) {
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.1", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("10.2", org.flywaydb.core.internal.license.Edition.PRO);
        recommendFlywayUpgradeIfNecessary("10.4");
    } else {
        ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition("5.7", org.flywaydb.core.internal.license.Edition.ENTERPRISE);
        if (JdbcUtils.getDriverName(jdbcMetaData).contains("MariaDB")) {
            LOG.warn("You are connected to a MySQL " + getVersion() + " database using the MariaDB driver." +
                    " This is known to cause issues." +
                    " An upgrade to Oracle's MySQL JDBC driver is highly recommended.");
        }
        recommendFlywayUpgradeIfNecessary("8.0");
    }
}

protected final void ensureDatabaseNotOlderThanOtherwiseRecommendUpgradeToFlywayEdition(String oldestSupportedVersionInThisEdition, Edition editionWhereStillSupported) {
    if (!getVersion().isAtLeast(oldestSupportedVersionInThisEdition)) {
        throw new FlywayEditionUpgradeRequiredException(
                editionWhereStillSupported,
                databaseType,
                computeVersionDisplayName(getVersion()));
    }
}

2.7.5 版本比较

  • flyway把版本都封装在org.flywaydb.core.api.MigrationVersion类里
  • 比较的原理
    • 先把版本中的下划线(_)替换成点(.),不支持其它非数字字符,有的话会报错;
    • 把版本以点进行分隔成多个数字(点分隔的每段一个数字);
    • 把每一段数字转成Bigdecimal,即版本的每段数字都可以很大,即使精确到毫秒的时间戳作为版本也支持;
    • 两个版本之间进行比较
      • 取两个版本中段数量大的段数作为循环次数;
      • 循环比较,从左到右,每一段相比(Bidecimal相比),某段大的则版本大,若段数不够则用0代替;
  • 注意
    • 每段都是数字值相比,不是按字符串相比
    • 段数的多或少并不能说明版本的大小,比如并不是段数多的版本大;
    • 段是从左到右比的,哪段先大就版本大;

2.8 日志

  • flyway本身提供了日志的方式,但实现也比较混乱
    • 比如可以通过命令行参数指定-outputFile=xxx或-logFile=xxx来输出日志文件,通过指定-X或-q来分别控制日志的级别为DEBUG或WARN,但这些参数生效的前提是在环境中找不到slf4j等常用的日志方式才会生效
    • 这些日志的格式无法控制,打印出来的日志时间没有日期
  • 使用logback
    • 依赖slf4j相关的三个包:logback-classic-1.2.3.jar、logback-core-1.2.3.jar、slf4j-api-1.7.25.jar
      • java代码工程环境直接依赖即可
      • 命令行环境则需要把这些包放到${INSTALLDIR}/lib,可能也可以用其它方式依赖(待研究);
    • logback.xml文件路径
      • 在java代码工程环境中,如果是使用springboot环境,则直接放到classpath中即可找到(springboot有自己找logback的方式),否则依赖slf4j的查找方式(待研究);
      • 在命令行环境中,可在java命令启动的时候加入-Dlogback.configurationFile=xxx/logback.xml 参数来指定logback.xml文件(不确定是否有更优雅的方式,待研究)
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章