Spring Boot数据库版本控制工具技术方案

Posted CuteXiaoKe

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot数据库版本控制工具技术方案相关的知识,希望对你有一定的参考价值。

1. 数据库版本困境

  在大家的日常开发过程中,肯定使用过代码版本控制工具,并且使用得十分熟练,但是对于数据库,有没有进行版本控制呢?我猜大部分企业和开发者都没有使用到数据库版本工具,如果没有使用,可能会遇到如下问题:

  1. 当前环境下的数据库处于什么状态?
  2. 多环境下数据库版本是否保持一致呢?例如刚刚修复线上的数据库变更是否应用到了测试和开发环境?
  3. 我们编写的数据库语句是否已经执行了呢?
  4. 在新的环境下,怎么快速设置一个新的数据库实例呢?
  5. 特定软件代码版本与数据库 Schema 的兼容问题怎么解决?

  其实现在的数据版本控制工具成熟的工具很多,在这里,我向大家推荐一款适合于Java开发者的工具——Flyway,在这里我就不过多介绍了,大家可以点击官网。引入了Flyway工具以后,我们可以结合spring boot来解决上述问题,优化数据库部署。

spring boot在父pom中会有引入flyway这个第三方库,大家如果对版本号没什么要求的话,即可立即引用,由此可见flyway这个工具是很成熟的,有着广阔的生态圈。

2. 关键步骤

2.1 样例数据库及版本变化

图 2.1.1 数据库版本变迁样例

  本次样例的环境如下:

  • spring boot: 1.5.18.RELEASE
  • jdk: 1.8
  • alibaba-druid: 1.1.10
  • flyway:5.2.4

  如果您没有相关flyway基础,可以从第三章核心概念和原理开始看。

2.2 (可选)定义基础版本

  如果当前数据库已经存在表格,依旧是项目介入flyway的时候数据库已经存在(如上图5.0版本),中途接入的话需要在项目的application.properties或者application.yml等配置文件中加入如下配置:

flyway:
  baseline-on-migrate: true  # 当迁移时发现目标schema非空,而且带有没有元数据的表时,是否自动执行基准迁移,默认false.
  baseline-version: 5.1 # 开始执行基准迁移时对现有的schema的版本打标签,默认值为1.

2.3 定义5.1版本sql

  在maven中引入flyway-core后,在maven resources目录下db/migration文件下新建V5.1.0.20210302.09.16__init_ddl.sql,内容如下:

ALTER TABLE `test`.`ke_order`
ADD COLUMN `creator_name` varchar(50) NULL COMMENT '创建者姓名' AFTER `creator_id`;

2.4 定义5.2版本sql

  新建 V5.2.0.20210310.13.45__新增地址表 并插入数据.sql,内容如下:

DROP TABLE IF EXISTS `address`;
CREATE TABLE `address`  (
  `id` bigint(20) NOT NULL COMMENT '主键id',
  `address_name` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci NULL DEFAULT NULL COMMENT '地址名称',
  `longtitude` decimal(20, 7) NULL DEFAULT NULL COMMENT '经度',
  `latitude` decimal(20, 7) NULL DEFAULT NULL COMMENT '维度',
  `gmt_create` timestamp(0) NULL DEFAULT CURRENT_TIMESTAMP(0) COMMENT '创建时间',
  `gmt_modified` timestamp(0) NULL DEFAULT NULL COMMENT '修改时间',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8 COLLATE = utf8_general_ci ROW_FORMAT = Dynamic;

SET FOREIGN_KEY_CHECKS = 1;

2.5 启动项目

  启动spring boot项目,控制台没有输出异常日志,可以在数据库已经执行了上述两个sql文件,并在flyway_schema_history表中存在执行记录,如图2.5.1所示:

图2.5.1 启动项目数据库迁移执行成功

2.6 异常分支流程

  是不是感觉很简单,但是,这只是主流程,在日常大多数情况都不会出现异常,但是由于是方案设计,必须要考虑到各种异常情况,如果在生产环境部署中出现了异常,就应该有相对的补救措施,下面列举常见的异常分支情况:

2.6.1 sql定义错误

  假如在V5.1.0.20210302.09.16__init_ddl.sql文件中输入了错误的sql语句,比如如下语句:

sssss;

ALTER TABLE `test`.`ke_order`
ADD COLUMN `creator_name` varchar(50) NULL COMMENT '创建者姓名' AFTER `creator_id`;

  那么会在项目初次启动的过程中报错提示,如图 2.6.1.1所示:

图 2.6.1.1 数据文件定义错误导致项目启动报错

  同时在flyway_schema_histroy表中会记录执行的记录,如图 2.6.1.2

图 2.6.1.2 数据文件定义错误在数据库里面的记录

如果V5.1.0.1__init_ddl.sql文件执行错了以后,我们需要在修正sql文件的同时,把版本对应的数据库记录删除掉再启动项目

2.6.2 修改已存在(迁移)的sql

  如果有其他开发者不小心修改了已存在的数据库迁移脚本,那么flyway会检查文件的哈希值,如果变更在项目启动的过程中会报异常,如图2.6.2.1所示:

图2.6.2.1 修改已存在sql启动异常

2.6.3 同版本有多个sql文件

  如果一个版本存在多个sql文件,包括同版本号不同描述,那么会如图2.6.3.1所示:

图2.6.3.1 同版本有多个sql文件

2.6.4 发现有数据库迁移没有应用到数据库

  此异常主要出现于多人开发模式中,如果他人定义迁移版本大于你定义的数据库版本,并且已应用到数据库中,你们出现Caused by: org.flywaydb.core.api.FlywayException: Validate failed: Detected resolved migration not applied to database: 5.1.0.20210302.14.06

2.6.5 存在有错误的迁移

  如果在flyway_schema_history表中某个迁移的success=0的话,就说明这个迁移执行失败,可以通过flyway repair命令或者手动删除该条记录的方式来修正。

2.6.6 常见异常编码汇总

异常编码需求产生原因解决方法
FAULT一个未知错误联系flyway团队,或者提交GitHub issue
VALIDATE_ERROR一些迁移部分设备只有设备编号无设备名称,所有模块中的设备名称补齐(包括GIS的搜索)
FAILED_VERSION_MIGRATION存在失败的版本迁移修正数据库迁移,并执行repair命令或者手动删除数据库记录
FAILED_REPEATABLE_MIGRATION存在失败的可重复迁移修正数据库迁移,并执行repair命令或者手动删除数据库记录
CHECKSUM_MISMACTCH即将应用的迁移和已经被应用的迁移校验和不一样修正回即将应用的迁移,或者取舍留哪个文件

   更多异常请阅读参考资料2。

3. 核心概念和原理

3.1 迁移(Migrations)

  在flyway中,迁移指的是任何对数据库变更就是迁移,迁移通常可以区分为版本迁移可重复迁移:

  • 版本迁移:拥有一个版本号,描述和校验和。版本号必须唯一,描述可以让你清晰地明白这个迁移是干什么的,校验和主要用来检验文件/脚本是否变更。版本迁移是最常用的迁移,他们**有序执行并且只会执行一次。**版本迁移可以细分为两种形式:
    • 常规迁移(regular): 通常用来创建表、数据更新、建立外检等;
    • 撤销迁移(undo):与常规迁移相反,对同版本号的常规迁移进行回退;
  • 可重复迁移:有一个描述和校验和,没有版本号。与版本迁移只会运行一次不同,可重复迁移只要校验和变了就会被再次执行,常见用途:
    • (重新)创建视图、存储过程等;
    • 大量数据重新插入;

  版本迁移和可重复迁移大部分中通过SQL语句来定义,但是还有其他形式可以定义,可支持的定义形式如下:

  1. SQL语句;
  2. JAVA语句,把相关变更通过java语句的方式写入.java文件;
  3. 其他脚本,例如.psl,.bat,.cmd,.sh,.bash,.py等形式;

JAVA语句方式很方便,可以手动写代码,尤其是在分布式环境下需要调用外部接口,或者一些复杂的插入更新逻辑的初始化操作中及其有用。

  在版本迁移中,版本号的定义和使用如图3.1.1所示:

图3.1.1 同版本有多个sql文件

3.2 flyway关键指令

3.2.1 Migrate

  把数据库迁移到最新的版本,如果sechema history table不存在的话就会创建,也就是之前提到过的flyway_schema_histroy表,如图3.2.1.1所示:

图3.2.1.1 migrate指令

3.2.2 Clean

  在配置的数据库schema中清除所有对象,包括表、视图、存储过程、触发器,这个操作慎重操作哈。如图3.2.2.1所示:

图3.2.2.1 clean指令

3.2.3 Info

  打印出所有迁移的细节和状态,如图3.2.3.1所示:

图3.2.3.1 info指令

3.2.4 Validate

  验证已应用的迁移和本地迁移是否有变更,以防止对已应用的迁移进行变更,如图3.2.4.1所示:

图3.2.4.1 validate指令

3.2.5 Undo

  执行迁移撤销动作,对应之前讲述的撤销迁移,如图3.2.5.1所示:

图3.2.5.1 undo指令

3.2.6 Baseline

  对应现有的数据库进行基线操作,指定基线版本,如图3.2.6.1所示:

图3.2.6.1 baseline指令

3.2.7 Repair

  修复flyway_schema_history表中的错误,也就是之前提到过status=0的字段,还有重新对齐现有迁移的校验和、描述等,如图3.2.7.1所示:

图3.2.7.1 repair指令

3.3 迁移失败回滚

  Flyway在一个单独的事务中运行每个迁移。如果失败,此事务将回滚。但是对于DDL的事务支持的话,不是所有数据库都OK的,所以如果把DDL和DML写在一个迁移文件里面,如果执行失败了,会导致整体不会回滚,所以通常建议DDL和DML分开写在迁移文件里面。

听说mysql 8.0开始也支持DDL事务了,有兴趣的大家可以去看看

4. 实施方案

  先说说业内比较通用的项目交付流程,如图4.1所示,从本地->开发->测试->生产,各个环境的版本会不一致。

图4.1 项目交付流程

  在我们公司,会严格按照这个流程走,如果线上出现什么问题,会先在开发环境里面开发人员自己先测试,然后交给测试人员,测试完毕以后再上生产,而不能在生产上面直接改。

4.1 开发阶段

  对开发者来说,使用flyway数据库版本工具,主要是关注点如果协同定义数据库,很有可能会出现冲突,所以规范相关操作是重中之重,整体流程如图4.1.1下:

图 4.1.1 开发者数据库变更流程

  其中如果你们本地开发使用的是本地数据库服务,那合入到开发环境的时候就不会有任何问题;如果你们像我这边一样本地和开发环境使用的都是中心数据库,并且支持开发者本地调试,那么在实际过程中可能会遇到如图4.1.2所示的问题:

图 4.1.2 开发者数据库变更流程

  为了克服上述问题需要进行如下配置:

flyway:
	out-of-order: true #是否开启乱序执行,不开启的话flyway只允许执行版本号递增的迁移
  ignoreMissingMigrations: true #是否忽略丢失的迁移,在本地调试下,会丢失其他开发者定义的数据库迁移

更多spring boot相关配置请阅读参考资料1或者其他官方的配置详情,在这里我就不详细展开水数字了23333

4.2 测试/生产部署

  对于运维部署人员,整体流程则比较简单,如图4.2.1所示:

图 4.2.1 运维人员部署流程

4.3 其他交付流程

  考虑到有些公司如果线上出现问题以后会直接在线上的版本上修正,而我们开发环境和测试环境在开发其它版本,这时候需要把应用的迁移应用到开发和测试环境中,可以参照参考资料3的方案,可以分为两步走:

  1. 首先我们在特定环境下定义一个更高版本的sql文件;
  2. 然后我们需要重写应用的sql语句,使之能保证可重复运行和幂等性,举个栗子就是插入数据前先检查数据是否存在,如果存在则不做任何事情。

不同数据库的可重复运行和幂等性的sql语句定义方式不同,例如mysql要使用存储过程来检查列是否存在。

  在这里我就不过多扩展了,不然越讲越多了,收不住了>_<,有兴趣的可以在下面留言,大家一起讨论一下。

5. 不同场景下的数据库版本控制

  • 如何处理database-specific sql(依赖数据库的sql语句),可以使用flyway.locations配置项;
  • 零停机下的数据库迁移、大数据迁移,请阅读参考资料3
  • 多schema下的flyway,请阅读参考资料4/5
  • 需要使用上线后使用代码进行数据初始化的场景,这时候我们只需通过定义java代码的迁移变更即可

6. 参考资料

[1] Flyway数据库版本控制器理解与使用
[2] flyway error codes
[3] Database migration with Flyway in large project
[4] Does Flyway support multiple schemas
[5] multiple-datasources-migrations-using-flyway-in-a-spring-boot-application

以上是关于Spring Boot数据库版本控制工具技术方案的主要内容,如果未能解决你的问题,请参考以下文章

Spring Boot 集成 Flyway,数据库也能做版本控制,太牛逼了!

Spring Boot集成Flyway实现数据库版本控制?

Spring Boot中使用Flyway来管理数据库版本

spring boot集成quartz scheduler

基于Spring Boot+Security+Redis权限管理系统,权限控制采用RBAC

什么是 Spring Boot 版本控制约定?