一、为什么要用Flyway
目前开发中存在以下问题:
- 开发、测试、生产存在多套数据库环境,对于同一数据库的修改,需要手动执行多次;
- 数据库变更无记录,投产上线前梳理SQL脚本容易遗漏。
为解决上述问题,尝试使用Flyway.
二、Flyway简介
- Flyway是一款开源的数据库版本管理工具,支持在所有环境中进行稳健的架构演变。
- Flyway具有幂等性,SQL脚本对数据库对影响只是一次性的,不会重复操作。
- Flyway独立于应用实现,对数据库版本进行管理,支持数据库版本的自动升级。
三、Flyway运行机制
引用Flyway官网的图进行说明:
Flyway通过记录数据库版本升级来进行管理。数据库中的每一个修改后状态对应数据库的一个版本。理论上Flyway引入后,不允许直接操作数据库,均需通过Flyway配置扫描路径下的SQL文件来操作。
Flyway运行成功后,会在数据库中自动创建 flyway_schema_history(表名可通过配置修改)表,该表用于记录数据库的变更记录。
- 表中每一条记录对应扫描路径(一般配置在 /src/resources/db/migration)下的每一个SQL文件执行;
- 表字段 version 对应 SQL 脚本版本;
- 表字段 script 对应 SQL 脚本名;
- 表字段 success 对应 SQL 脚本执行结果,0: 失败;1: 成功。
四、Flyway 配置使用(包括多数据源配置)
Flyway的配置特别简单,在数据库连接配好的前提下,仅需要引入依赖、增加application配置即可。
1、pom中增加依赖
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>5.2.1</version>
</dependency>
2、进行application相关flyway配置
本项目配置使用application.yaml,在yaml中增加flyway配置
flyway:
baseline-on-migrate: true
enabled: true
locations: classpath:db/migration
table: flyway_schema_history
本项目配置的比较简单,配置的参数主要含义为:
baseline-on-migrate:当schema为空时,是否自动执行基线。配置为true时,第一次启动flyway时,会自动创建flyway记录版本的表flyway_schema_history,以及基线记录(V1基线记录)
enabled:是否开启flyway
locations:扫描的sql脚本位置
- 支持项目路径(classpath)、文件路径(filesystem)等格式。如果不显示声明,默认为classpath:db/migration
- 路径支持模糊匹配,通配符支持:
– ** :0个或多个路径。例如 db/**/test(确定以 /test 结尾),db/path1/test、db/path1/path2/test 均可以匹配,但是 db/path1/test1 就不行了
– * :0个或多个字符。例如 db/version1.* ,db/version1.0、db/version1.123456 均可以匹配,但是 db/version2.0 不能匹配
– ?:1个字符。例如 db/version1.?,db/version1.0、db/version1.2 均可匹配,但是 db/version1.11 就不可以
table:自动生成的记录版本表名
更多flyway参数说明可参考官网:flyway官网文档地址
3、多数据源配置
随着项目的扩大,引入了多数据源连接(目前使用druid配置的多数据源连接),同时需引入多数据源的版本配置。
Flyway自身是无法区分数据源连接的,为支持多数据源版本管理,需将多数据源对应的SQL文件分别存放,并增加flyway配置。
本项目中,主数据源相关迁移脚本放在/resources/db/migration 目录下,另一数据源迁移脚本放在 /resources/db/baseMigration 目录下。
主数据源flyway配置无需修改,再增加一套flyway配置即可。
主数据源flyway配置在application.yaml中,另一数据源无法同时配置在applicaiton中,增加 FlywayConfig.java 配置文件,主要代码如下:
@Configuration
@EnableTransactionManagement
@Import({DynamicDataSourceRegister.class})
public class FlywayConfig {
private static final String BASE_SQL_LOCATION = "/db/baseMigration";
private static final String ENCODING = "UTF-8";
// application配置文件中base数据源配置
@Value("${frame.druid.ds.base.url}")
private string baseUrl;
@Value("${frame.druid.ds.base.username}")
private string baseUserName;
@Value("${frame.druid.ds.base.password}")
private string basePassword;
@Value("${frame.druid.ds.base.driverClassName}")
private string baseDriverClassName;
@Bean
public void migrate() {
Flyway flyway = Flyway.configure()
.dataSource(getBaseDataSource())
.locations(BASE_SQL_LOCATION)
.encoding(ENCODING)
.baselineOnMigrate(true)
.table("base_flyway")
.load();
flyway.migrate();
}
private BasicDataSource getBaseDataSource() {
BasicDataSource dataSource = new BasicDataSource();
dataSource.setUrl(baseUrl);
dataSource.setUserName(baseUserName);
dataSource.setPassword(basePassword);
dataSource.setDriverClassName(baseDriverClassName);
return dataSource;
}
}
配置完成后,启动项目,两个路径下的迁移脚本均完成执行。
五、Flyway 使用规范
SQL脚本命名规范
扫描路径下SQL文件命名为 V2_1__init_request.sql 格式,具体说明如下:
- V 为固定前缀分隔符,代表数据库版本化;
- 2_1 为 SQL 脚本版本,’_’ 翻译为小数点,2_1 即为 2.1 版本;
- __为两个下划线,代表中间分隔符;
- init_request 为 SQL 脚本名,概述本脚本要进行的操作;
- .sql 为固定后缀。
开发规范
通过在项目中配置,Flyway 会在 application 启动时自动按顺序执行扫描路径下的 SQL 文件。开发中建议遵循以下规范:
- 禁止修改已执行的 SQL 文件(已执行的定义:SQL文件已合并到公共分支,且 flyway_schema_history 表里已有该条记录),如需修改已执行 SQL 涉及的表,需新增 SQL 文件写入修改语句;
- DDL 与 DML 语句不能写在同一 SQL 文件;
- 在增加 SQL 脚本后,需先在本地进行启动 applicaiton。若数据迁移脚本执行失败,则 application 无法启动,请根据第六点进行修复;
- 禁止提交执行失败的 SQL 文件。本地新增 SQL 文件后,application 启动成功后,再提交代码到远程。
六、脚本执行失败排查及修复
1、由于Flyway导致application执行失败时,在console 中查看错误日志及原因
例如图中失败原因为 存储过程delete_matches 已经存在,则需要删除已存在的存储过程,再次执行。
2、根据console中的错误提示,修复SQL脚本。
3、然后数据库中打开 flyway_schema_history 表,删除 success 字段为 0 (0状态为失败,1状态为成功) 的记录(表中最后一条记录)。
再次启动 application即可。
最后,让我们开心、愉快的来使用 Flyway 吧~