目录
一、摘要
二、概览
三、Flyway工作步骤
四、与springboot集成
4.1 加入依赖
4.2 添加配置
4.3 添加SQL
4.4 添加启动类
4.5 验证结果
4.6 注意事项
五、局限性
5.1 版本号冲突
5.2 执行顺序
我们开发或升级项目会碰到很多数据有关问题,比如:这个环境找不到原始的DDL记录、这个环境是否运行了最新的SQL、这个环境运行了哪些SQL、每次更新发版需要先写脚本更新SQL再更新应用、对于老旧项目数据升级或迁移异常困难...
针对这些问题,我们都需要需要一个记录并运行SQL的工具。我们可以手动开发一个工具,用数据表记录项目变更运行的每句SQL,再和项目特定SQL目录下的SQL语句对比,然后再执行那些未执行过的SQL。当然,目前也有这种开源的SQL版本工具,这就是Flyway。
当然Flyway并不是很好,比如SQL执行顺序以及版本号小的不执行会有很多问题。
Flyway是个数据库版本管理工具,但我觉得它更像是个SQL版本管理工具。它提供命令行和Java API用于管理数据库表,主要通过项目SQL与数据库里记录运行过的SQL对比来执行SQL,以保持各个环境的数据库表是一致的。其官方文档地址为:Documentation - Flyway by Redgate • Database Migrations Made Easy.
1、项目启动,它会连接数据库并寻找一个名为flyway_schema_history的历史记录表,若找不到历史记录表则会创建它。
2、Flyway扫描文件系统或应用程序类路径(默认为:classpath:db/migration)下的SQL文件,并与历史记录表里的执行过的SQL记录对比,若项目的SQL与执行过的SQL不一致,则报错,项目停止运行。
3、若校验通过,则将版本号(版本号在SQL文件名中指定)大于历史记录表里的最大版本号的SQL文件按从小到大顺序运行。
具体pom依赖如下:
org.flywaydb
flyway-core
6.5.7
org.springframework.boot
spring-boot-starter-web
2.7.9
mysql
mysql-connector-java
8.0.28
com.baomidou
mybatis-plus-boot-starter
3.5.2
注意不要选太高版本的flyway-core,高版本的flyway-core兼容性不太好,实测9.16.1版本会报异常: java.lang.NoSuchMethodError: org.flywaydb.core.api.configuration.FluentConfiguration.ignoreMissingMigrations(Z)Lorg/flywaydb/core/api/configuration/FluentConfiguration;
server:
port: 9090
spring:
mvc:
pathmatch:
matching-strategy: ant_path_matcher
application:
name: boot-flyway
datasource:
driver-class-name: com.mysql.cj.jdbc.Driver
url: jdbc:mysql://localhost:3306/test?characterEncoding=utf-8&useSSL=false&serverTimezone=GMT%2B8&allowMultiQueries=true
username: root
password: root
jackson:
date-format: yyyy-MM-dd HH:mm:ss
time-zone: GMT+8
serialization:
write-dates-as-timestamps: false
# flyway配置
flyway:
# 是否启用flyway
enabled: true
# 编码格式,默认UTF-8
encoding: UTF-8
# 迁移sql脚本文件存放路径,默认db/migration
locations: classpath:db,classpath:db/alarm,classpath:db/workflow
# 迁移sql脚本文件名称的前缀,默认V
sql-migration-prefix: V
# 迁移sql脚本文件名称的分隔符,默认2个下划线__
sql-migration-separator: __
# 前缀和分隔符之间就是版本号,版本号可以用.或_分隔,比如V20230403__add_user.sql、V2023.4.3__add_user.sql、V2023_4_3__add_user.sql的版本号分别是:20230403、2023.4.3、2023_4_3
# 迁移sql脚本文件名称的后缀
sql-migration-suffixes: .sql
# 迁移时是否进行校验,默认true
validate-on-migrate: true
# 当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表
baseline-on-migrate: true
mybatis-plus:
mapper-locations: classpath:/mapper/**/*Mapper.xml
type-aliases-package: com.longqi.boot.flyway.entity.domain
configuration:
log-impl: org.apache.ibatis.logging.stdout.StdOutImpl
在指定位置添加SQL文件,上面配置了三个位置,因此三个位置都可以添加SQL,但是,所有SQL文件的版本号不能相同!
注意SQL的命名规则,SQL文件有执行一次和重复执行两种。SQL命名规则如下:
1、执行一次的SQL:文件名以V开头,格式为 V+版本号+双下划线+文件描述+后缀。其中版本号可以使用“.”或“_”分隔开。比如V1.2_20230404__update_model.sql,其中1.2_20230404就是版本号,update_model就是描述。
2、可以重复执行的SQL:文件名以R开头,其它和上面执行一次的SQL类似,执行优先级比执行一次的SQL低。
package com.longqi.boot.flyway;
import org.mybatis.spring.annotation.MapperScan;
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
/**
* @Description TODO
* @Author LongQi
* @Date 2021/6/20 12:01
*/
@SpringBootApplication
@MapperScan(basePackages = {"com.longqi.boot.flyway.dao"})
public class FlywayApplication {
public static void main(String[] args) {
SpringApplication.run(FlywayApplication.class, args);
}
}
启动类就是普通的springboot启动类,无需做任何特殊处理,Flyway会自动读取spring.flyway相关的配置以及校验运行SQL脚本。
启动后数据库如下:
可以看到,运行了SQL,flyway的历史记录表就有记录,然后SQL也都是运行成功。
应用一般不会出问题,但SQL需要注意以下几点:
1、历史SQL一旦运行后,不能修改,否则校验不通过,会抛异常导致应用无法启动!
2、SQL的版本号不能相同,否则校验不通过,会抛异常导致应用无法启动!
3、SQL的语句运行不能报错,否则运行SQL错误,会抛异常导致应用无法启动!
4、对于小于数据库最大版本号的SQL文件,是不会执行的!比如执行了0.03版本的SQL,后续添加了0.02_update版本的SQL,重新启动是不会执行0.02_update版本的SQL的!
SQL和应用已经牢牢的绑在一起了,SQL校验或运行出问题,都是会导致应用无法启动!且SQL对版本号有相当大的限制。生产环境都需要重视。
个人使用过程中,发现这个框架在每个应用有一个单独数据库的情况下很好使用,但对于目前微服务盛行的年代就不太好使了。
多个微服务共用一个数据库很常见,甚至不同的微服务由不同小组完成,而每个微服务都集成Flyway的话,可能会导致SQL版本号冲突,当然,各微服务的版本号冲突之类的也有解决办法,规范各微服务的SQL版本号命名即可。
我们知道,Flyway是不会执行比SQL最大版本号小的SQL文件的。而微服务共同发布又要一个版本运行多个SQL,这里只所以运行多个,是因为有些产品各微服务需要拆开卖给客户,因此,SQL不能集中在一起执行。
这里举例说明,比如某产品发布V1.2版,微服务A和微服务B的SQL分别为V1.2_A和V1.2_B,若上线时,A服务先启动运行还好,后续B服务启动运行判断 V1.2_B>V1.2_A,则V1.2_B可以执行。若是B服务先启动,A服务后启动就遭了,V1.2_A Flyway的设计者少考虑了微服务一起发版需要运行多个SQL的问题,有如下几种方法解决: 1、上线的启动脚本可以定义好启动顺序,上一个服务心跳检测成功后再进行启动下一个服务。 2、直接不用Flyway,自己写个类似Flyway的框架即可,再加个应用名判断,同应用名才有版本号大小判断执行SQL。 3、改造Flyway,可以给它创建表时加入应用名字段,插入数据以及判断都加上应用名判断,逻辑和上面类似,同应用名才有版本号大小判断执行SQL。 4、换用别的类似Flyway的框架。