Spring Boot数据库版本控制工具技术方案
Posted CuteXiaoKe
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Spring Boot数据库版本控制工具技术方案相关的知识,希望对你有一定的参考价值。
1. 数据库版本困境
在大家的日常开发过程中,肯定使用过代码版本控制工具,并且使用得十分熟练,但是对于数据库,有没有进行版本控制呢?我猜大部分企业和开发者都没有使用到数据库版本工具,如果没有使用,可能会遇到如下问题:
- 当前环境下的数据库处于什么状态?
- 多环境下数据库版本是否保持一致呢?例如刚刚修复线上的数据库变更是否应用到了测试和开发环境?
- 我们编写的数据库语句是否已经执行了呢?
- 在新的环境下,怎么快速设置一个新的数据库实例呢?
- 特定软件代码版本与数据库 Schema 的兼容问题怎么解决?
其实现在的数据版本控制工具成熟的工具很多,在这里,我向大家推荐一款适合于Java开发者的工具——Flyway,在这里我就不过多介绍了,大家可以点击官网。引入了Flyway工具以后,我们可以结合spring boot来解决上述问题,优化数据库部署。
spring boot在父pom中会有引入flyway这个第三方库,大家如果对版本号没什么要求的话,即可立即引用,由此可见flyway这个工具是很成熟的,有着广阔的生态圈。
2. 关键步骤
2.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.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所示:
同时在flyway_schema_histroy表中会记录执行的记录,如图 2.6.1.2:
如果V5.1.0.1__init_ddl.sql文件执行错了以后,我们需要在修正sql文件的同时,把版本对应的数据库记录删除掉再启动项目
2.6.2 修改已存在(迁移)的sql
如果有其他开发者不小心修改了已存在的数据库迁移脚本,那么flyway会检查文件的哈希值,如果变更在项目启动的过程中会报异常,如图2.6.2.1所示:
2.6.3 同版本有多个sql文件
如果一个版本存在多个sql文件,包括同版本号不同描述,那么会如图2.6.3.1所示:
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语句来定义,但是还有其他形式可以定义,可支持的定义形式如下:
- SQL语句;
- JAVA语句,把相关变更通过java语句的方式写入.java文件;
- 其他脚本,例如
.psl
,.bat
,.cmd
,.sh
,.bash
,.py
等形式;
JAVA语句方式很方便,可以手动写代码,尤其是在分布式环境下需要调用外部接口,或者一些复杂的插入更新逻辑的初始化操作中及其有用。
在版本迁移中,版本号的定义和使用如图3.1.1所示:
3.2 flyway关键指令
3.2.1 Migrate
把数据库迁移到最新的版本,如果sechema history table不存在的话就会创建,也就是之前提到过的flyway_schema_histroy表,如图3.2.1.1所示:
3.2.2 Clean
在配置的数据库schema中清除所有对象,包括表、视图、存储过程、触发器,这个操作慎重操作哈。如图3.2.2.1所示:
3.2.3 Info
打印出所有迁移的细节和状态,如图3.2.3.1所示:
3.2.4 Validate
验证已应用的迁移和本地迁移是否有变更,以防止对已应用的迁移进行变更,如图3.2.4.1所示:
3.2.5 Undo
执行迁移撤销动作,对应之前讲述的撤销迁移,如图3.2.5.1所示:
3.2.6 Baseline
对应现有的数据库进行基线操作,指定基线版本,如图3.2.6.1所示:
3.2.7 Repair
修复flyway_schema_history表中的错误,也就是之前提到过status=0的字段,还有重新对齐现有迁移的校验和、描述等,如图3.2.7.1所示:
3.3 迁移失败回滚
Flyway在一个单独的事务中运行每个迁移。如果失败,此事务将回滚。但是对于DDL的事务支持的话,不是所有数据库都OK的,所以如果把DDL和DML写在一个迁移文件里面,如果执行失败了,会导致整体不会回滚,所以通常建议DDL和DML分开写在迁移文件里面。
听说mysql 8.0开始也支持DDL事务了,有兴趣的大家可以去看看
4. 实施方案
先说说业内比较通用的项目交付流程,如图4.1所示,从本地->开发->测试->生产,各个环境的版本会不一致。
在我们公司,会严格按照这个流程走,如果线上出现什么问题,会先在开发环境里面开发人员自己先测试,然后交给测试人员,测试完毕以后再上生产,而不能在生产上面直接改。
4.1 开发阶段
对开发者来说,使用flyway数据库版本工具,主要是关注点如果协同定义数据库,很有可能会出现冲突,所以规范相关操作是重中之重,整体流程如图4.1.1下:
其中如果你们本地开发使用的是本地数据库服务,那合入到开发环境的时候就不会有任何问题;如果你们像我这边一样本地和开发环境使用的都是中心数据库,并且支持开发者本地调试,那么在实际过程中可能会遇到如图4.1.2所示的问题:
为了克服上述问题需要进行如下配置:
flyway:
out-of-order: true #是否开启乱序执行,不开启的话flyway只允许执行版本号递增的迁移
ignoreMissingMigrations: true #是否忽略丢失的迁移,在本地调试下,会丢失其他开发者定义的数据库迁移
更多spring boot相关配置请阅读参考资料1或者其他官方的配置详情,在这里我就不详细展开水数字了23333
4.2 测试/生产部署
对于运维部署人员,整体流程则比较简单,如图4.2.1所示:
4.3 其他交付流程
考虑到有些公司如果线上出现问题以后会直接在线上的版本上修正,而我们开发环境和测试环境在开发其它版本,这时候需要把应用的迁移应用到开发和测试环境中,可以参照参考资料3的方案,可以分为两步走:
- 首先我们在特定环境下定义一个更高版本的sql文件;
- 然后我们需要重写应用的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,数据库也能做版本控制,太牛逼了!