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
值。下图表示了多个数据库版本。
对应记录表:
表中记录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 模式来解决。
这三种的命名规则如下图:
- Prefix 可配置,前缀标识,默认值
V
表示 Versioned,R
表示 Repeatable,U
表示 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 进行数据库版本控制了。这里总结了一些在实际开发中的使用经验:
- 生产务必禁
spring.flyway.cleanDisabled=false
。 - 尽量避免使用 Undo 模式。
- 开发版本号尽量根据团队来进行多层次的命名避免混乱。比如
V1.0.1__ProjectName_{Feature|fix}_Developer_Description.sql
,这种命名同时也可以获取更多脚本的开发者和相关功能的信息。 spring.flyway.outOfOrder
取值 生产上使用false
,开发中使用true
。- 多个系统公用一个 数据库
schema
时配置spring.flyway.table
为不同的系统设置不同的metadata
表名而不使用缺省值flyway_schema_history
。
9.目前遇到的问题
- 新建新版本的sql版本文件,项目启动后不能再更新,这会导致在一次开发中需要使用多个版本文件。目前解决的办法可以删除对应metadata数据库对应的最新版本数据(奇技淫巧)。
- sql文件的语句必须是可重复执行的,比如插入数据,数据插入后,如果遇到情况1,重新执行对应数据库文件,如果sql文件不能重新执行,项目启动时会出现sql语法错误,项目启动失败。
10.总结
在进行了如上的实验后,相信我们都已经掌握了flyway的初步使用。目前还是在探索阶段,实际开发中还没有使用起来,后续肯定还会遇到很多问题,本博客会依据实际情况,持续不断更新。