flyway 这一篇够了

midoll 728 2022-07-05

什么是 flyway

  • Flyway is an open-source database migration tool. It strongly favors simplicity and convention over configuration.

flyway 是一款开源的数据库版本管理工具,它更倾向于规约优于配置的方式(约定大于配置)。flyway 可以独立于应用实现管理并跟踪数据库变更,支持数据库版本自动升级,并且有一套默认的规约,不需要复杂的配置,Migrations 可以写成 SQL 脚本,也可以写在 Java 代码中,不仅支持 Command Line 和 Java API,还支持 Build 构建工具和 Spring Boot 等,同时在分布式环境下能够安全可靠地升级数据库,同时也支持失败恢复等。

为什么要使用 flyway

对于软件部分,我们已经有成熟的解决方案:Git/SVN 等版本控制系统;GoCD/Jenkins/CircleCI 等持续集成工具;蓝绿部署等成熟的自动化部署方案。但是对于数据库部分呢?很多项目仍然依赖于手动运行 SQL 脚本,甚至还在项目中处处可见用于补丁升级的 SQL 脚本。那么问题就来了:如何确认各个环境上的数据库状态?某个 SQL 脚本是否运行过了? flyway 数据库迁移工具就是帮助我们解决这个问题的。
zjqznguucg5n

当前我们的数据库管理现状

我们遇到的问题:
不同的开发人员在开发产品特性时,都有可能更新数据库(添加新表,新的约束等)。所有的数据脚本都需要写成可重复执行的形式,统一放于 svn 中管理,由于没有状态的记录,每次都会执行全部的脚本,一不小心就容易出现问题,如果是新增数据脚本用于初始化基本数据,那我们基本是让运维手工执行 sql 脚本,以什么顺序来执行这些脚本,都需要填写和运维沟通清楚,这些问题同样存在于生产环境。

flyway 优势

对比其他工具的最大优势:就是 简单且易于管理,而且直接书写 SQL 并不需要额外的学习( 这里我们选择 flyway 有两个原因,一是它是 Java 生态圈的,其次就是 Spring Boot 提供了内建支持,可以很快应用到产品中 )

flyway 是如何工作的?

第一个最简单的场景:如何使用 flyway 从无到有的创建数据库。
101144252598
flyway 用 flyway_schema_history 数据表存放执行 SQL 脚本的历史记录,跟踪数据库结构的变更;我们则需要在项目中定义 Migration ,通常用 SQL 或 Java 定义。如下图所示,flyway 在运行时会顺序执行上图中的 Migration1 和 Migration2 来实现对数据库的更新;同时 flyway_schema_history 表也会记录下这两次修改。

101144209523

  • 注意:
    flyway 因为版本不同,旧版本的 flyway 的执行记录表不是 flyway_schema_history 而是 schema_version ,而且表结构也发生了变更,在选择 flyway 版本的时候还需注意!
    接下来就是第二个场景:基于已有数据库进行更新。flyway 仍然会遍历项目中定义的各个 Migration,并参照 flyway_schema_history 数据表,忽略版本号低于或等于当前版本的 Migration,剩下的就是 Pending Migration,然后按照版本号顺序执行 Pending Migration,如下图所示:
    101143558825
    因此,每当我们要对数据库的 DDL 或者 DML 进行演进,就只需要定义一个更高版本的 Migration

flyway 命令介绍

  • Repair
    Repair 操作能够修复 flyway_schema_history 表,该操作在 flyway_schema_history 表出现错误时是非常有用的。
    101143758428
    Repair 会修复 flyway_schema_history 表的错误,通常有两种用途:
    移除失败的 Migration 记录,该问题只是针对不支持 DDL 事务的数据库。
    重新调整已经应用的 Migratons 的 checksum 值,比如:某个 Migration 已经被应用,但本地进行了修改,又期望重新应用并调整 checksum 值,不过尽量不要这样操作,否则可能造成其它环境失败。
  • Migrate
    Migrate 是指把数据库 schema 迁移到最新版本,是 flyway 工作流的核心功能, flyway 在 Migrate 时会检查 flyway_schema_history 表,如果不存在会创建 flyway_schema_history 表, flyway_schema_history 表主要用于记录版本变更历史以及 checksum 之类的。
    101143952599
    migrate 时会扫描指定文件系统或 classpath 下的 migrations (可以理解为数据库的版本脚本),并且会逐一比对数据表中的已存在的版本记录,如果有未应用的 migrations,flyway 会获取这些 migrations 并按次序应用到数据库中,否则不需要做任何事情。另外,通常在应用程序启动时应默认执行 migrate 操作,从而避免程序和数据库的不一致性。
  • Validate
    validate 是指验证已经应用的 migrations 是否有变更,flyway 是默认是开启验证的。
    110114405654
    validate 原理是对比 flyway_schema_history 表与本地 migrations 的 checksum 值,如果值相同则验证通过,否则验证失败,从而可以防止对已经应用到数据库的本地 migrations 的无意修改。
  • Clean
    clean 相对比较容易理解,即清除掉对应数据库 schema 中的所有对象,包括表结构,视图,存储过程,函数以及所有的数据等都会被清除。
    110114392922
    所以,执行 clean 命令的时候,还请三思!
    flyway 的使用命令介绍就到这里了。当然,flyway 的命名不只有这些,如果你感兴趣的话,可以自行到官网查看

在项目中如何引入并使用 flyway

flyway 引入

其他几种引入方式,详见 |- flyway -| 项目中接入 flyway 的几种方式 ,以下以 自定义 starter 为例
1、pom 文件引入 flyway 相应依赖
starter 和接入项目在文末也会给出相应的项目连接地址

<dependency>
	 <groupId>org.flywaydb</groupId>
 	<artifactId>flyway-core</artifactId>
 	<version>6.0.7</version>
</dependency>
<!-- 引入自定义的 flyway starter -->
<dependency>
	<groupId>com.linkr.flyway</groupId>
	<artifactId>linkr-starter-flyway</artifactId>
 <version>1.0.0-SNAPSHOT</version>
</dependency>

2、yml 文件中的配置如下
配置中心的配置信息:

flywaydb:
  url: ${spring.datasource.url}
  username: username
  password: password

注意:
要求数据库账号 除了拥有增删改查操作外还需拥有能够操作数据表的权限,因为第一次引进 flyway的时候,会在数据库中创建一张名为 flyway_schema_history 的数据表,该表主要用于记录 SQL 脚本的执行记录和版本信息
url: 直接引用 当前项目中的数据库连接即可
username: 配置成加密后的字符串
password: 配置成加密后的字符串
添加数据库脚本
脚本存放位置(默认):src/resources/db/migration(目录是可以自定义,参数配置见 使用须知 )
101143455289

SQL 数据脚本命名须知

不可重复执行脚本
V{version}_{time}${name}.sql,其中前缀V、后缀.sql、分隔符

可重复执行脚本
R__V{version}_{time}${name}.sql,其中前缀V、后缀.sql、分隔符
注意 脚本执行顺序
「 可重复执行脚本(R)」 的执行顺序在 「 不可重复执行脚本(V)」 之后(此处有坑)
执行顺序的重要性(依靠版本号来约束):如果是进行数据脚本迁移且现有的数据库被清空的情况下(即空数据库情况下),那么建表语句需先执行(最小版本号)才行。如果建表语句写成可重复脚本的话,那么程序将会报错,因为「 可重复执行脚本(R)」 的执行顺序在 「 不可重复执行脚本(V)」 之后,所以此时数据表还没建立,对于数据表中的数据操作都将报错
至此 flyway 引入完成,启动项目,坐等执行成功即可~

使用须知

关于 yml 配置支持

spring:
  application:
    name: flyway-db
  datasource:
    driver-class-name: com.mysql.jdbc.Driver
    url: jdbc:mysql://localhost:3306/iot_db_user?useUnicode=true&useSSL=false&characterEncoding=utf-8
    username: balabala
    password: balabala
# 模拟配置中心的配置
flywaydb:
  url: ${spring.datasource.url}
  username: username
  password: password
#  以下配置可以配置在 当前项目中
#  配置是否启动项目的时候,执行 repair 和 migrate 方法,用于自测;一般不需要配置
#  startEnable: false
#  如果有自定义需求,可以通过 locations 配置 SQL 脚本的存放目录,支持多个目录,用 “,” 隔开
#  locations: db/migration/V1_0_0,db/migration/V2_0_0

关于脚本的变更的问题

  • 1、修改 SQL 脚本
    如果直接更改其中一个执行过的 SQL 脚本,然后去执行 migrate 命令,会报错
    由于初始化脚本的改动,导致 flyway 校验失败,flyway 检测到当前的脚本与上一次执行的内容不同,提示报错并终止程序,以免造成更严重的数据结构破坏

101143351353
由报错可以看出,报错是因为 checksum 值和之前的不一样了,flyway 就是通过这个值来判断这个文件是否已经执行过的。

  • 解决方法
    使用 flyway repair 命令
    这个命令会修改错误的 SQL 文件校验码的值,再执行 migrate 就没问题了,但是,修改的内容还是不会被执行!
    因为 flyway_schema_history 数据表的 success 值为 1 ,便不会在重新执行脚本
  • 2、删除 SQL 脚本
    在开发阶段,已经写了 V1_0_0_20191024102411__add_userInfo.sql ,也已经 migrate,但是之后发现这个脚本没用,删掉了,后续再执行migrate时,发现会报错,
    110114332495
  • 解决办法
    注意:一般遇到这种情况,是由于自己的使用不当造成的,开发过程应该按照流程来,flyway 是不支持降级的,所以,即便你发现这个 SQL 没用,你不应该删除,你应该补充一个 SQL ,将你之前那个 SQL 中的语句逻辑上回退掉(如:你之前是创建了表,那么这个 SQL 就是删掉这个表)
  • 不合规的解决办法
    在数据库中,找到你指定的 flyway 的 flyway_schema_history 表,将你要删掉 SQL 文件的那条记录删除掉,重新执行 migrate 就可以了。 不支持这样做

注意
由于在 starter 项目中设置了在启动项目的时候会默认执行 repair 和 migrate 命令,以保证程序的正常运行,所以,请不要再次更改已经执行过的 SQL 脚本,虽然更改了运行也没有问题,但是你的修改信息也是不会生效的,反而会导致 SQL 脚本影响到其他环境的数据

项目地址:
starter 项目地址:https://github.com/QACBoy/linkr-starter-flyway
flywaydb 项目地址:https://github.com/QACBoy/flywaydb
参考链接
https://flywaydb.org/documentation/
https://www.jianshu.com/p/783b87871551
https://blog.csdn.net/xiang__liu/article/details/82780201
https://yq.aliyun.com/articles/269164?spm=a2c4e.11155472.0.0.4ecc3ee3quZEGC


# flyway