flyway使用

一、Flyway是什么

官网解释地非常全面,可先大致阅读一下。

简单地说,flyway是一个能对数据库变更做版本控制的工具。

二、为什么要用Flyway

在多人开发的项目中,我们都习惯了使用SVN或者Git来对代码做版本控制,主要的目的就是为了解决多人开发代码冲突和版本回退的问题。

其实,数据库的变更也需要版本控制,在日常开发中,我们经常会遇到下面的问题:

  1. 自己写的SQL忘了在所有环境执行;
  2. 别人写的SQL我们不能确定是否都在所有环境执行过了;
  3. 有人修改了已经执行过的SQL,期望再次执行;
  4. 需要新增环境做数据迁移;
  5. 每次发版需要手动控制先发DB版本,再发布应用版本;
  6. 其它场景...

有了flyway,这些问题都能得到很好的解决。

三、如何使用Flyway

3.1 准备数据库

首先,我们需要准备好一个空的数据库。(数据库的安装和账密配置此处忽略)

此处以mysql为例,在本地电脑上新建一个空的数据库,名称叫做flyway,我们通过dbeaver看到的样子如下:

新建一个空的数据库

3.2 准备SpringBoot工程

在start.spring.io上新建一个SpringBoot工程,要求能连上自己本地新建的mysql数据库flyway,这个步骤也比较简单,就不再细讲。

但要注意的是,application.yml中数据库的配置务必配置正确,下述步骤中系统启动时,flyway需要凭借这些配置连接到数据库。这里贴一份:

server:
  port: 8080
spring:
  application:
    name: flyway-demo
  datasource:
    druid: # 使用druid数据源
      url: jdbc:mysql://127.0.0.1:3306/flyway?serverTimezone=GMT%2B8&useSSL=false&useUnicode=true&characterEncoding=UTF-8
      username: root
      password: 123456
      driver-class-name: com.mysql.cj.jdbc.Driver
      filters: stat # 配置监控统计拦截的filters
      initial-size: 10 # 初始化大小,最小,最大
      min-idle: 8
      max-active: 20
      max-wait: 60000 # 配置获取连接等待超时的时间
      time-between-eviction-runs-millis: 60000 # 配置间隔多久才进行一次检测,检测需要关闭的空闲连接,单位是毫秒
      min-evictable-idle-time-millis: 300000 # 配置一个连接在池中最小生存的时间,单位是毫秒
      validation-query: select 'x'
      test-while-idle: true
      test-on-borrow: false
      test-on-return: false
      pool-prepared-statements: true
      max-open-prepared-statements: 20
      stat-view-servlet:
        login-username: admin #druid监控用户名
        login-password: admin #druid监控密码
  flyway:
    # 是否启用flyway
    enabled: true
    ## 编码格式,默认UTF-8
    encoding: UTF-8
    ## 迁移sql脚本文件存放路径,默认db/migration
    locations: classpath:db/migration
    ## 迁移sql脚本文件名称的前缀,默认V
    sqlMigrationPrefix: V
    ## 迁移sql脚本文件名称的分隔符,默认2个下划线__
    sqlMigrationSeparator: __
    # 迁移sql脚本文件名称的后缀
    sqlMigrationSuffixes: .sql
    # 迁移时是否进行校验,默认true
    validateOnMigrate: true
    # 设置为true,当迁移发现数据库非空且存在没有元数据的表时,自动执行基准迁移,新建schema_version表
    baselineOnMigrate: true

相关属性

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

多数据源时需要配置(如读写分离)

需要配置 url、user、password,如使用shardingsphere框架

  flyway:
    url: ${spring.shardingsphere.datasource.master.jdbc-url}
    user: ${spring.shardingsphere.datasource.master.username}
    password: ${spring.shardingsphere.datasource.master.password}

3.3 flyway的引入与尝试

首先,在pom文件中引入flyway的核心依赖包:

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

其次,在src/main/resources目录下面新建db.migration文件夹,默认情况下,该目录下的.sql文件就算是需要被flyway做版本控制的数据库SQL语句。

但是此处的SQL语句命名需要遵从一定的规范,否则运行的时候flyway会报错。命名规则主要有两种:

  1. 仅需要被执行一次的SQL命名以大写的"V"开头,后面跟上"0~9"数字的组合,数字之间可以用“.”或者下划线"_"分割开,然后再以两个下划线分割,其后跟文件名称,最后以.sql结尾。比如,V2.1.5__create_user_ddl.sqlV4.1_2__add_user_dml.sql
  2. 可重复运行的SQL,则以大写的“R”开头,后面再以两个下划线分割,其后跟文件名称,最后以.sql结尾。。比如,R__truncate_user_dml.sql

其中,V开头的SQL执行优先级要比R开头的SQL优先级高。

如下,我们准备了三个脚本,分别为:

  1. V1__create_user.sql,其中代码如下,目的是建立一张user表,且只执行一次。

    CREATE TABLE IF NOT EXISTS `USER`(
        `USER_ID`          INT(11)           NOT NULL AUTO_INCREMENT,
    `USER_NAME`        VARCHAR(100)      NOT NULL COMMENT '用户姓名',
    `AGE`              INT(3)            NOT NULL COMMENT '年龄',
    `CREATED_TIME`     datetime          NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `CREATED_BY`       varchar(100)      NOT NULL DEFAULT 'UNKNOWN',
    `UPDATED_TIME`     datetime          NOT NULL DEFAULT CURRENT_TIMESTAMP,
    `UPDATED_BY`       varchar(100)      NOT NULL DEFAULT 'UNKNOWN',
    PRIMARY KEY (`USER_ID`)
    )ENGINE=InnoDB DEFAULT CHARSET=utf8;
    
  2. V2__add_user.sql,其中代码如下,目的是往user表中插入一条数据,且只执行一次。

    insert into `user`(user_name,age) values('lisi',33);
    
  3. R__add_unknown_user.sql,其中代码如下,目的是每次启动倘若有变化,则往user表中插入一条数据。

    insert into `user`(user_name,age) values('unknown',33);
    

与之相对应的目录截图如下:

项目目录结构

其中2.1.6、2.1.7和every的文件夹不会影响flyway对SQL的识别和运行,可以自行取名和分类。

到这一步,flyway的默认配置已经足够我们开始运行了。此时,我们启动SpringBoot的主程序,如果以上步骤没有配置错误的话,运行如下:

[INFO ] 2022-04-02 15:58:51.846 [main] o.f.c.i.license.VersionPrinter - Flyway Community Edition 7.15.0 by Redgate
[INFO ] 2022-04-02 15:58:51.847 [main] o.f.c.i.d.base.BaseDatabaseType - Database: jdbc:mysql://127.0.0.1:3306/flyway (MySQL 5.7)
[INFO ] 2022-04-02 15:58:51.899 [main] o.f.core.internal.command.DbValidate - Successfully validated 3 migrations (execution time 00:00.024s)
[INFO ] 2022-04-02 15:58:51.919 [main] o.f.c.i.s.JdbcTableSchemaHistory - Creating Schema History table `flyway`.`flyway_schema_history` ...
[INFO ] 2022-04-02 15:58:51.977 [main] o.f.core.internal.command.DbMigrate - Current version of schema `flyway`: << Empty Schema >>
[INFO ] 2022-04-02 15:58:51.987 [main] o.f.core.internal.command.DbMigrate - Migrating schema `flyway` to version "1 - create user"
[INFO ] 2022-04-02 15:58:52.026 [main] o.f.core.internal.command.DbMigrate - Migrating schema `flyway` to version "2 - add user"
[INFO ] 2022-04-02 15:58:52.044 [main] o.f.core.internal.command.DbMigrate - Migrating schema `flyway` with repeatable migration "add unknown user"
[INFO ] 2022-04-02 15:58:52.060 [main] o.f.core.internal.command.DbMigrate - Successfully applied 3 migrations to schema `flyway`, now at version v2 (execution time 00:00.092s)
[INFO ] 2022-04-02 15:58:52.126 [main] o.a.coyote.http11.Http11NioProtocol - Starting ProtocolHandler ["http-nio-8080"]

flyway成功运行

此时,我们刷新数据库,可以看到flyway的历史记录表已经生成并插入了三个版本的记录:

flyway_schema_history

而且,user表也已经创建好了并插入了两条数据:

我们不改变任何东西,再次执行主程序,日志如下:

[INFO ] 2022-04-02 16:02:13.863 [main] o.f.c.i.license.VersionPrinter - Flyway Community Edition 7.15.0 by Redgate
[INFO ] 2022-04-02 16:02:13.864 [main] o.f.c.i.d.base.BaseDatabaseType - Database: jdbc:mysql://127.0.0.1:3306/flyway (MySQL 5.7)
[INFO ] 2022-04-02 16:02:13.930 [main] o.f.core.internal.command.DbValidate - Successfully validated 3 migrations (execution time 00:00.035s)
[INFO ] 2022-04-02 16:02:13.942 [main] o.f.core.internal.command.DbMigrate - Current version of schema `flyway`: 2
[INFO ] 2022-04-02 16:02:13.943 [main] o.f.core.internal.command.DbMigrate - Schema `flyway` is up to date. No migration necessary.

两张数据库表中的内容也毫无任何变化。

可是,如果我们修改V2__add_user.sql中的内容,再次执行的话,就会报错,提示信息如下:

[ERROR] Migration checksum mismatch for migration version 2

如果我们修改了R__add_unknown_user.sql,再次执行的话,该脚本就会再次得到执行,并且flyway的历史记录表中也会增加本次执行的记录。

3.4 Flyway 的规则

Flyway 是如何比较两个 SQL 文件的先后顺序呢?它采用 采用左对齐原则, 缺位用 0 代替 。举几个例子:

1.0.1.1 比 1.0.1 版本高。
1.0.10 比 1.0.9.4 版本高。
1.0.10 和 1.0.010 版本号一样高, 每个版本号部分的前导 0 会被忽略。

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

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

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

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

四、maven 插件

每次想要migration都需要运行整个springboot项目,并且只能执行migrate一种命令,其实flyway还是有很多其它命令的。maven插件给了我们不需要启动项目就能执行flyway各种命令的机会。

在pom中引入flyway的插件,同时配置好对应的数据库连接。

<plugin>
    <groupId>org.flywaydb</groupId>
    <artifactId>flyway-maven-plugin</artifactId>
    <version>${flyway.version}</version>
    <configuration>
        <url>jdbc:mysql://localhost:3306/flyway?useUnicode=true&amp;characterEncoding=utf8&amp;serverTimezone=GMT</url>
        <user>root</user>
        <password>123456</password>
        <driver>com.mysql.cj.jdbc.Driver</driver>
    </configuration>
</plugin>

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

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

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

migrate

Migrate是指把数据库Schema迁移到最新版本,是Flyway工作流的核心功能,Flyway在Migrate时会检查Metadata(元数据)表,如果不存在会创建Metadata表,Metadata表主要用于记录版本变更历史以及Checksum之类的。

baseline

Baseline针对已经存在Schema结构的数据库的一种解决方案,即实现在非空数据库中新建Metadata表,并把Migrations应用到该数据库。
Baseline可以应用到特定的版本,这样在已有表结构的数据库中也可以实现添加Metadata表,从而利用Flyway进行新Migrations的管理了。

clean(慎用)

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

info

Info用于打印所有Migrations的详细和状态信息,其实也是通过Metadata表和Migrations完成的,Info能够帮助快速定位当前的数据库版本,以及查看执行成功和失败的Migrations。下图很好地示意了Info打印出来的信息。

repair

repair操作能够修复Metadata表,该操作在Metadata表出现错误时是非常有用的。

Repair会修复Metadata表的错误,通常有两种用途:
移除失败的Migration记录,该问题只是针对不支持DDL事务的数据库。
重新调整已经应用的Migratons的Checksums值,比如:某个Migratinon已经被应用,但本地进行了修改,又期望重新应用并调整Checksum值,不过尽量不要这样操作,否则可能造成其它环境失败。

undo

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

validate

Validate是指验证已经Apply的Migrations是否有变更,Flyway是默认是开启验证的。
Validate原理是对比Metadata表与本地Migrations的Checksum值,如果值相同则验证通过,否则验证失败,从而可以防止对已经Apply到数据库的本地Migrations的无意修改。

flyway补充知识

  • flyway执行migrate必须在空白的数据库上进行,否则报错;
  • 对于已经有数据的数据库,必须先baseline,然后才能migrate;
  • clean操作是删除数据库的所有内容,包括baseline之前的内容;
  • 尽量不要修改已经执行过的SQL,即便是R开头的可反复执行的SQL,它们会不利于数据迁移;

五、旧项目如何接入

5.1 执行baseline

>mvn baseline
[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------< com.ybw:flyway-demo >-------------------------
[INFO] Building flyway-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The POM for com.alibaba:druid:jar:1.2.6 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[INFO] 
[INFO] --- flyway-maven-plugin:7.15.0:baseline (default-cli) @ flyway-demo ---
[INFO] Flyway Community Edition 7.15.0 by Redgate
[INFO] Database: jdbc:mysql://localhost:3306/flyway (MySQL 5.7)
[INFO] Schema history table `flyway`.`flyway_schema_history` already initialized with (1,<< Flyway Baseline >>). Skipping.
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.446 s
[INFO] Finished at: 2022-04-02T16:50:42+08:00
[INFO] ------------------------------------------------------------------------

会创建flyway_schema_history表,初始化一条数据

影响

会把1版本占用,版本1sql就不会执行了。

 5.2 执行migrate

[INFO] Scanning for projects...
[INFO] 
[INFO] ------------------------< com.ybw:flyway-demo >-------------------------
[INFO] Building flyway-demo 1.0.0
[INFO] --------------------------------[ jar ]---------------------------------
[WARNING] The POM for com.alibaba:druid:jar:1.2.6 is invalid, transitive dependencies (if any) will not be available, enable debug logging for more details
[INFO] 
[INFO] --- flyway-maven-plugin:7.15.0:migrate (default-cli) @ flyway-demo ---
[INFO] Flyway Community Edition 7.15.0 by Redgate
[INFO] Database: jdbc:mysql://localhost:3306/flyway (MySQL 5.7)
[INFO] Successfully validated 5 migrations (execution time 00:00.025s)
[INFO] Current version of schema `flyway`: 1
[INFO] Migrating schema `flyway` to version "2 - add user"
[INFO] Migrating schema `flyway` to version "3 - add user"
[INFO] Migrating schema `flyway` to version "4 - add user"
[INFO] Migrating schema `flyway` with repeatable migration "add unknown user"
[INFO] Successfully applied 4 migrations to schema `flyway`, now at version v4 (execution time 00:00.113s)
[INFO] Flyway Community Edition 7.15.0 by Redgate
[INFO] ------------------------------------------------------------------------
[INFO] BUILD SUCCESS
[INFO] ------------------------------------------------------------------------
[INFO] Total time:  2.451 s
[INFO] Finished at: 2022-04-02T16:52:42+08:00
[INFO] ------------------------------------------------------------------------

flyway_schema_history生成新纪录

5.3 结论

  • 旧项目如果想记录以前的表结构、数据,可以放到版本1的sql里面。
  • 旧项目如果不用记录以前的表结构、数据,也可以不执行baseline,直接从1版本开始,新的ddl和dml。

6、团队使用

比如当前有两个团队A、B,2个版本在开发1.1.0、1.2.0,每个版本后再加一位数(第四位数),保证不同时间、不同人更新,版本号根据时间更新。

团队研发人员时间版本
A团队开发A1开发2021年5月1日V1.1.0.1__create_user.sql
A2开发2021年5月1日V1.1.0.2__create_person.sql
A1开发2021年5月2日V1.1.0.3__create_staff.sql
B团队开发B1开发2021年5月1日V1.2.0.1__create_order.sql
B2开发2021年5月1日V1.2.0.2__create_goods.sql
B1开发2021年5月2日V1.2.0.3__create_student.sql

版本号是根据时间升序,所以第四位数字可以是时间。

7、其他

注释

纠错

如果在开发过程中,需要修改V1.1.0.1的sql,当前最大版本为1.1.0.5

  1. 将flyway_schema_history对应的1.1.0.1记录删除。
  2. 将V1.1.0.1的sql文件改为V1.1.0.6。

flyway和Archery

两者结合使用。

  • flyway管理版本,ddl处理。
  • archery负责无版本上线,如不改表结构dml的的临时上线。

名词解释

DDL(Data Definition Language 数据定义语言)

Create语句:可以创建数据库和数据库的一些对象。

Drop语句:可以删除数据表、索引、触发程序、条件约束以及数据表的权限等。

Alter语句:修改数据表定义及属性。

DML(Data Manipulation Language 数据操控语言)

Insert语句:向数据表张插入一条记录。

Delete语句:删除数据表中的一条或多条记录,也可以删除数据表中的所有记录,但是,它的操作对象仍是记录。

Update语句:用于修改已存在表中的记录的内容。

flyway需要的权限

mysql5.7

GRANT SELECT ON  `performance_schema`.user_variables_by_thread TO ${user}@'%' identified by '${password}';
flush privileges;

mysql 8

GRANT Select ON `performance_schema`.user_variables_by_thread TO ${user}@`%`;
flush privileges;
  • ${user}为用户名。
  • ${password}为密码。

user_variables_by_thread(用户自定义变量记录表)

performance_schema提供了一个保存用户定义变量的user_variables_by_thread表(该表也保存由mysql内部链接线程建立的变量)。这些变量是在特定会话中定义的变量,变量名由@字符开头。

咱们先来看看表中记录的统计信息是什么样子的。

admin@localhost : performance_schema 01:50:16> select * from user_variables_by_thread;
+-----------+-------------------------+--------------------------------------+
| THREAD_ID | VARIABLE_NAME          | VARIABLE_VALUE                      |
+-----------+-------------------------+--------------------------------------+
|        45 | slave_uuid              | 4b0027eb-6223-11e7-94ad-525400950aac |
|        45 | master_heartbeat_period | 5000000000                          |
|        45 | master_binlog_checksum  | CRC32                                |
+-----------+-------------------------+--------------------------------------+
3 rows in set (0.01 sec)

表中各字段含义以下:

  • THREAD_ID:定义变量的会话的线程标识符(ID)

  • VARIABLE_NAME:定义的变量名称,在该表中去掉了@字符的形式显式

  • VARIABLE_VALUE:定义的变量值

user_variables_by_thread表不容许使用TRUNCATE TABLE语句。

版本交叉上线

如:1.5.0线上先线,1.4.0后上线。

1.4.0上线时,需要设置spring.flyway.out-of-order=true,1.4.0上线时,就不会报错了。

 1.4.0上线后,当前版本依然会是1.5.0。

Current version of schema `flyway`: 1.5.0

还原spring.flyway.out-of-order=false后,版本要高于1.5.0才行。

修改版本SQL

UPDATE flyway_schema_history 
SET version = REPLACE ( version, '1.0.0', '2.0.0' ),
script = REPLACE ( script, '1.0.0', '2.0.0' ) 
WHERE
	installed_rank = 38;
  • 1.0.0为被修改版本号
  • 2.0.0为修改版本号。

git与flyway的结合使用

当git开发为1.0.0时,数据库里的flyway版本为2.0.0时,启动是没有问题的。

不过如果1.0.0,继续添加sql,就需要将out-of-order=true。

8、低版本flyway

报错:Detected resolved migration not applied to database on flyway

解决方案:配置下面的属性即可。

spring.flyway.ignore-missing-migrations=true #忽略缺失的升级脚本验证

场景

背景

  • 多团队,多版本开发。
  • 数据库没有隔离:开发环境。
  • 数据库有隔离:测试环境,预生产环境,生产环境。

版本

团队A开发版本1.1,团队B开发版本1.2。

报错

只要团队A部署过测试环境,团队B就会报错。

原因:

  • B团队代码中没有1.1版本,只有1.2版本。
  • 1.1版本低于1.2版本。

解决办法

开发环境进行如下配置

spring.flyway.ignore-missing-migrations=true #忽略缺失的升级脚本验证

总结

  • 数据库隔离:测试环境不同版本部署不同环境,就不会发生这种错误。
  • 部署环境只有一套:预生产、生产环境只有一套环境,入口只有一个,也不会发生这种错误。
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值