From version control to continuous delivery, Flyway helps individuals, teams, and enterprises build on application delivery processes to automate database development. 

1.引言

在项目开发中,一直在探索如何进行数据库的版本管理。关注的公众号推送了数据库版本管理工具Flyway,Flyway支持的数据库为  Oracle,  SQL Server (including Amazon RDS and Azure SQL Database),  Azure Synapse (Formerly Data Warehouse),  DB2,  MySQL  (including Amazon RDS, Azure Database & Google Cloud SQL),  Aurora MySQL,  MariaDB,  Percona XtraDB Cluster,  TestContainers,  PostgreSQL  (including Amazon RDS, Azure Database, Google Cloud SQL, TimescaleDB, YugabyteDB & Heroku),  Aurora PostgreSQL,  Redshift,  CockroachDB,  SAP HANA,  Sybase ASE,  Informix,  H2,  HSQLDB,  Derby,  Snowflake,  SQLite  and  Firebird.

最近实际操作了一下,结合官方文档,记录一下如何应用于Spring Boot项目中。

2.工作机制

Flyway默认需要在数据库中新建一个metadata数据表,默认表名为flyway_schema_history,在该表中保存着每次 migration (迁移)的记录, 记录包含 migration 脚本的版本号和 SQL 脚本的 checksum 值。下图表示了多个数据库版本。

preview

对应记录表:

表中记录5个版本,对应配置文件下(第三节 的locations值),五个文件,如图所示。

 Flyway 扫描文件系统或应用程序的类路径读取 DDL 和 DML 以进行迁移。根据metadata 表进行检查迁移。如果脚本声明的版本号小于或等于标记为当前版本的版本号之一,将忽略它们。其余迁移是待处理迁移:可用,但未应用。最后按版本号对它们进行排序并按顺序执行 并将执行结果写入 metadata 表。 

3.SQL文件命名规则

Flyway 将 SQL 文件分为 Versioned 、Repeatable 和 Undo 三种:

  • Versioned 用于版本升级, 每个版本有唯一的版本号并只能执行一次.
  • Repeatable 可重复执行, 当 Flyway检测到 Repeatable 类型的 SQL 脚本的 checksum 有变动, Flyway 就会重新应用该脚本. 它并不用于版本更新, 这类的 migration 总是在 Versioned 执行之后才被执行。
  • Undo 用于撤销具有相同版本的版本化迁移带来的影响。但是该回滚过于粗暴,过于机械化,一般不推荐使用。一般建议使用 Versioned 模式来解决。

这三种的命名规则如下图:

naming.png

  • Prefix 可配置,前缀标识,默认值 V 表示 VersionedR 表示 RepeatableU 表示 Undo
  • Version 标识版本号, 由一个或多个数字构成, 数字之间的分隔符可用点 . 或下划线 _
  • Separator 可配置, 用于分隔版本标识与描述信息, 默认为两个下划线 __
  • Description 描述信息, 文字之间可以用下划线 _ 或空格 分隔
  • Suffix 可配置, 后续标识, 默认为 .sql

4.项目引入

<!--    数据库版本管理工具 定义properties    -->
<flyway.version>8.5.13</flyway.version>


<dependency>
     <groupId>org.flywaydb</groupId>
     <artifactId>flyway-core</artifactId>
     <version>${flyway.version}</version>
</dependency>
<dependency>
      <groupId>org.flywaydb</groupId>
      <artifactId>flyway-mysql</artifactId>
      <version>${flyway.version}</version>
</dependency>

注意:因为我使用的数据库为MySql版本现在Flyway不直接支持,需要添加对于mysql的支持引入。

5. 配置文件

application.yml配置属性

spring:
  datasource:
    #???????
    driver-class-name: com.mysql.cj.jdbc.Driver
    url: ${DATABASE_SYSTEM_URL}
    username: ${DATABASE_SYSTEM_USERNAME}
    password: ${DATABASE_SYSTEM_PASSWORD}
  #数据库版本管理工具flyway配置
  flyway:
    schemas: leading_info_collection  # 数据库名称
    enabled: true #是否开启flyway
    encoding: utf-8 #编码方式
    clean-on-validation-error: false #当发现校验错误时是否自动调用clean,默认false.
    baseline-on-migrate: true # 如果数据库不是空表,需要设置成 true,否则启动报错
    baseline-version: 0 # 与 baseline-on-migrate: true 搭配使用
    clean-disabled: true  # 禁止清理数据库表
    locations: classpath:db/migration #设定 SQL 脚本的目录,多个路径使用逗号分隔, 比如取值为                 
    #classpath:db/migration,filesystem:/sql-migrations
    out-of-order: true #是否按照版本顺序执行sql文件
    validate-on-migrate: false #启动时是否校验最新的版本迁移文件

此处列出了比较重要的一些配置,具体配置可以查看官方文档。这里讲解一下重要的配置。

flyway.baseline-description对执行迁移时基准版本的描述.
flyway.baseline-on-migrate当迁移时发现目标schema非空,而且带有没有元数据的表时,是否自动执行基准迁移,默认false.
flyway.baseline-version开始执行基准迁移时对现有的schema的版本打标签,默认值为1.
flyway.check-location检查迁移脚本的位置是否存在,默认false.
flyway.clean-on-validation-error当发现校验错误时是否自动调用clean,默认false.
flyway.enabled是否开启flywary,默认true.
flyway.encoding设置迁移时的编码,默认UTF-8.
flyway.ignore-failed-future-migration当读取元数据表时是否忽略错误的迁移,默认false.
flyway.init-sqls当初始化好连接时要执行的SQL.
flyway.locations迁移脚本的位置,默认db/migration.
flyway.out-of-order是否允许无序的迁移,默认false.
flyway.password目标数据库的密码.
flyway.placeholder-prefix设置每个placeholder的前缀,默认${.
flyway.placeholder-replacementplaceholders是否要被替换,默认true.
flyway.placeholder-suffix设置每个placeholder的后缀,默认}.
flyway.placeholders.[placeholder name]设置placeholder的value
flyway.schemas设定需要flywary迁移的schema,大小写敏感,默认为连接默认的schema.
flyway.sql-migration-prefix迁移文件的前缀,默认为V.
flyway.sql-migration-separator迁移脚本的文件名分隔符,默认__
flyway.sql-migration-suffix迁移脚本的后缀,默认为.sql
flyway.tableflyway使用的元数据表名,默认为schema_version
flyway.target迁移时使用的目标版本,默认为latest version
flyway.url迁移时使用的JDBC URL,如果没有指定的话,将使用配置的主数据源
flyway.user迁移数据库的用户名
flyway.validate-on-migrate迁移时是否校验,默认为true

6.实践

6.1 初次启动项目

如下图所示,数据库中没有任何表,现在有V0版本,V0版本为初始化数据表

DROP TABLE IF EXISTS `problem_info`;
CREATE TABLE `problem_info` (
       `id` bigint NOT NULL AUTO_INCREMENT COMMENT '用户ID',
       `create_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP COMMENT '创建时间',
       `update_time` datetime NOT NULL DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '更新时间',
       `subject` varchar(255) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '描述主题',
       `subjection_model` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '所属模块',
       `detail` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '具体描述',
       `ImageNames` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '图片地址',
       `verificationCode` varchar(1024) CHARACTER SET utf8mb4 COLLATE utf8mb4_unicode_ci DEFAULT '' COMMENT '修改删除使用的验证码',
       PRIMARY KEY (`id`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=144 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_ci COMMENT='问题信息表';

 启动项目,控制台打印日志如下:

此时数据库新建了 flyway_schema_history表,并新建了一条数据,执行了initdb.sql文件,新建了problem_info表格。

6.2 新增版本

现在按照命名规则,新增 V1版本添加一个学生的数据表,重新启动项目。

CREATE TABLE student (
     id INT(4),
     name VARCHAR(20),
     sex CHAR(1));

 

6.3 更改版本文件报错

现在更改V1__add_student_table.sql文件。

CREATE TABLE student (
     id INT(4),
     name VARCHAR(20),
     sex CHAR(1));

DROP PROCEDURE IF EXISTS `sp_select_one_age_dogs`;

delimiter $
CREATE PROCEDURE sp_select_one_age_dogs()
begin
select * from dog d where d.dog_age <=1;
end $

再重新启动项目,报错信息如下。 

6.4 重复执行SQL文件

新建一个以R开头的可重复执行的SQL文件R__add_procedure.sql,重新启动项目。

 

7.Maven插件

再pom.xml文件中添加对应的Maven插件,配置好数据库连接。

 <plugin>
                <groupId>org.flywaydb</groupId>
                <artifactId>flyway-maven-plugin</artifactId>
                <version>${flyway.version}</version>
                <configuration>
                    <url>${env.DATABASE_SYSTEM_URL}</url>
                    <user>${env.DATABASE_SYSTEM_USERNAME}</user>
                    <password>${env.DATABASE_SYSTEM_PASSWORD}</password>
                    <driver>com.mysql.cj.jdbc.Driver</driver>
                </configuration>
            </plugin>

更新maven插件列表,就可以看到flyway的全部命令了。

此时,我们双击执行上图中的flyway:migrate的效果和启动整个工程执行migrate的效果是一样的。

其它命令的作用如下列出,各位可自行实验体会:

  • baseline,对已经存在数据库Schema结构的数据库一种解决方案。实现在非空数据库新建MetaData表,并把Migrations应用到该数据库;也可以在已有表结构的数据库中实现添加Metadata表。

  • clean,清除掉对应数据库Schema中所有的对象,包括表结构,视图,存储过程等,clean操作在dev 和 test阶段很好用,但在生产环境务必禁用。

  • info,用于打印所有的Migrations的详细和状态信息,也是通过MetaData和Migrations完成的,可以快速定位当前的数据库版本。

  • repair,repair操作能够修复metaData表,该操作在metadata出现错误时很有用。

  • undo,撤销操作,社区版不支持。

  • validate,验证已经apply的Migrations是否有变更,默认开启的,原理是对比MetaData表与本地Migrations的checkNum值,如果值相同则验证通过,否则失败。

8.最佳实践

通过上面的介绍相信你很快就会使用 Flyway 进行数据库版本控制了。这里总结了一些在实际开发中的使用经验:

  1. 生产务必禁 spring.flyway.cleanDisabled=false 。
  2. 尽量避免使用 Undo 模式。
  3. 开发版本号尽量根据团队来进行多层次的命名避免混乱。比如 V1.0.1__ProjectName_{Feature|fix}_Developer_Description.sql ,这种命名同时也可以获取更多脚本的开发者和相关功能的信息。
  4. spring.flyway.outOfOrder 取值 生产上使用 false,开发中使用 true
  5. 多个系统公用一个 数据库 schema 时配置spring.flyway.table 为不同的系统设置不同的 metadata 表名而不使用缺省值 flyway_schema_history 。

9.目前遇到的问题

  1. 新建新版本的sql版本文件,项目启动后不能再更新,这会导致在一次开发中需要使用多个版本文件。目前解决的办法可以删除对应metadata数据库对应的最新版本数据(奇技淫巧)。
  2. sql文件的语句必须是可重复执行的,比如插入数据,数据插入后,如果遇到情况1,重新执行对应数据库文件,如果sql文件不能重新执行,项目启动时会出现sql语法错误,项目启动失败。

10.总结

在进行了如上的实验后,相信我们都已经掌握了flyway的初步使用。目前还是在探索阶段,实际开发中还没有使用起来,后续肯定还会遇到很多问题,本博客会依据实际情况,持续不断更新。

 

 

 

 

Logo

腾讯云面向开发者汇聚海量精品云计算使用和开发经验,营造开放的云计算技术生态圈。

更多推荐