SQL脚本难以维护,各环境数据库版本不一致该怎么办?

821 阅读7分钟

一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第27天,点击查看活动详情

一、Flyway是什么?

通常情况下,数据库的版本控制一般都是通过代码管理工具统一管理SQL脚本,但是仅仅是将脚本与代码一起管理, 在应用升级时仍然会碰到很多问题:

  • 无法确认在某台机器上的数据库版本
  • 无法确认是否遗漏了某个数据库脚本未执行
  • 无法确认在某台机器上执行过哪些数据库脚本
  • 代码版本库无法管理数据库脚本依赖关系

所以,我们需要借助专业的数据库版本管理工具实现数据库的版本控制。Flyway就是一个数据库版本控制工具。 Flyway 是独立于数据库的应用、管理并跟踪数据库变更的数据库版本管理工具。

官网地址:flywaydb.org

Flyway主要有以下功能:

  • Flyway可以自动检测指定目录下的数据升级文件并升级至指定版本
  • Flyway可以自动检测指定目录下的数据回滚文件并回滚至指定版本(专业版功能)
  • Flyway可以检测已执行过的数据升级文件是否有改动及是否有错误
  • Flyway可随时展示当前数据库版本和已执行过的数据库升级
  • Flyway可以依照数据库脚本文件命名规则依次执行数据库脚本

二、Flyway工作原理

2.1 场景一:使用Flyway从无到有创建数据库

image.png

Flyway用'schema_version_history'数据表存放数据库schema的历史记录,跟踪数据库结构的变更;

由于刚开始数据库为空,Flyway找不到schema_version_history数据表,所以Flyway找不到它,就在数据库中创建了此表。

之后我们则需要在项目中定义Migration,通常用SQL或Java定义。

如下图所示,Flyway在运行时会顺序执行上图中的Migration1和Migration 2来实现对数据库的更新;同时'schema_version_history'表也会记录下这两次修改。

image.png

'schema_version_history'表记录修改历史。如下图所示:

installed_rankversiondescriptiontypescriptchecksuminstalled_byinstalled_onexecution_timesuccess
11Initial SetupSQLV1__Initial_Setup.sql1996767037axel2016-02-04 22:23:00.0546true
22First ChangesSQLV2__First_Changes.sql1279644856axel2016-02-06 09:18:00.0127true

2.2 场景二:基于已有数据库更新

在此场景下,Flyway仍然会遍历项目中定义的各个Migration,并参照schema_version_history数据表,忽略版本号低于或等于当前版本的Migration,

剩下的就是Pending Migration(待处理迁移版本),然后按照版本号顺序执行Pending Migration,如下图所示:

image.png

然后按版本号对它们进行排序并按顺序执行:

image.png

'schema_version_history'表记录修改历史。如下图所示:

flyway_schema_history
installed_rank描述类型脚本校验和已安装已安装执行时间处理时间成功
1个1个初始设置的SQLV1__Initial_Setup.sql19967670372016-02-04 22:23:00.0546真正
22第一次变化的SQLV2__First_Changes.sql12796448562016-02-06 09:18:00.0127真正
32.1重构JDBCV2_1__重构2016-02-10 17:45:05.4251真正

就是这样!每当需要发展数据库时,无论是结构(DDL)还是参考数据(DML),都只需创建一个新迁移,其版本号就会高于当前版本。下次Flyway启动时,它将找到它并相应地升级数据库。

三、Flyway的常用命令

3.1 Migrate

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

image.png

3.2 Clean

Clean相对比较容易理解,即清除掉对应数据库Schema中的所有对象,包括表结构,视图,存储过程,函数以及所有的数据等都会被清除(慎用)

image.png

3.3 Info

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

image.png

3.4 Validate

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

image.png

3.5 Baseline

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

3.6 Repair

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

image.png

3.7 在db/migration目录下创建需要更新的SQL,命名规则为:

image.png

四、与springBoot的集成

pom.xml依赖

<dependency>
  <groupId>org.flywaydb</groupId>
  <artifactId>flyway-core</artifactId>
  <version>5.0.7</version>
</dependency>
<dependency>
  <groupId>org.springframework.boot</groupId>
  <artifactId>spring-boot-starter-jdbc</artifactId>
</dependency>
<dependency>
  <groupId>mysql</groupId>
  <artifactId>mysql-connector-java</artifactId>
</dependency>

在application.properties或者application.yml文件添加配置:

  # 说明,在spring boot 1.x中,属性前缀为flyway,在spring boot 2.x中为spring.flyway,这里需要区分不同版本
  Spring.flyway:
  # 到新的环境中数据库中有数据,且没有flyway_schema_history表时,是否执行迁移操作。    如果设置为false,在启动时会报错,并停止迁移;    如果设置为true,则生成history表并完成所有的迁移,要根据实际情况设置;
  baseline-on-migrate: false
  # 执行时标记的tag 默认为<<Flyway Baseline>>
  baseline-description: <<Flyway Baseline>>
  # 是否启用flyway
  enabled: true
  # 检测迁移脚本的路径是否存在,如不存在,则抛出异常
  check-location: true
  # 脚本位置
  locations: classpath:db/migration
  # 在迁移时,是否校验脚本,假设V1.0__初始.sql已经迁移过了,在下次启动时会校验该脚本是否有变更过,则抛出异常
  validate-on-migrate: true
   特别说明:
   如果非空数据库迁移,在目标数据库中手动建flyway_schema_history表并手动写入初始化的脚本记录,   使flyway跳过最初的校验即可,后续可以保证版本的统一;

其他属性含义如下图所示:

image.png

在springBoot中引用即可:

image.png

五、注意事项

5.1 数据脚本的命名规则:

V+版本号++脚本名称+后缀 例如:V1.1__create_table.sql 其中flyway.sql-migration-prefix配置前缀,默认V flyway.sql-migration-separator配置分隔符,默认 flyway.sql-migration-suffix配置脚本后缀,默认.sql 特别注意:V1__.sql == V1.0__.sql V1.1__.sql == V1.1.0__.sql 所以配置时,最好设置显示递增的版本号,否则会报错

5.2 基线版本号和脚本文件版本号的关系

文件的版本号必须 > 基线初始版本号,否则不会执行你的脚本 比如基线默认版本号为1,所以你的脚本版本号必须大于1,例如V1.1****

5.3 脚本文件的特殊性:

A 如果项目已经执行了过了某个脚本,那么这个脚本不能删除,也不能修改,否则在项目启动时会报错,删除了则是找不到以前执行的文件,修改了则是在对比checksum时报不一致。所以如果在sql脚本有问题,第一次跑没有成功,重新跑时,要么重新定义脚本的版本号,要么删除表schema_version的当前版本记录 B 两个脚本的版本号应该严格不同,不能出现1中的特别注意项

5.4 地雷配置项:

flyway.clean-on-validation-error:这个配置项一定要小心了,如果配置为true,当你的sql脚本执行失败时,就会执行删除库中所有表的操作,即之前的clean操作,所以一定要慎重,慎重,慎重!!!

5.5 Druid 与 Flyway 的冲突

Flyway通过 SQL 脚本来执行数据库的建立与更新。当同时集成了 Druid 和 Flyway 之后,Druid 的 wall 防火墙极可能直接干预 SQL 脚本的操作,继而导致 Flyway 执行中断。在项目开发的过程中,配置了以下防火墙属性以放行 Flyway 的 SQL 操作。

spring:
  datasource:
    druid:
      wall:
        config:
          variantCheck: false
          noneBaseStatementAllow: true
          commentAllow: true
          multiStatementAllow: true