为什么使用flyway
一般的部署流程
- 开发人员将应用程序打包、按顺序汇总并整理数据库升级脚本
- DBA拿到数据库升级脚本检查、备份、执行,以完成数据库升级
- 应部署人员拿到应用部署包,备份、替换,以完成应用程序升级
用上 Flyway 之后的应用部署流程大概是这样的:
- 开发人员将应用程序打包(直接jenkins piplen 流程线构建 这时候flyway就已经在对应的环境执行了,脚本已经在库里生成了)
- 应部署人员拿到应用部署包,备份、替换,以完成应用程序升(Flyway将自动执行升级/备份脚本)—> 我们这边是Jenkins ci集成化构建部署,dockerfile构建镜像 ,k8s容器编排,在ci的过程中脚本就已经执行了,就不需要部署人员再去操作数据库了。
在我平时的团队开发中,我们的代码是有版本控制的,可以多人协作持续集成(工具也非常多)。但是数据库的脚本确实需要手动的去执行,团队内人多,你执行你的我执行我的,就会导致很多问题:
- 这台机器上的数据库处于什么状态?
- 此脚本是否已应用?
- 测试环境的数据库表结构和生产是否一致?
- 测试环境要新增一台应用实例,该如何搭建数据库,必备的数据和表结构如何快速同步?
所以工作中,经常听到测试小伙伴们抱怨,明明已经提测了,但是却出现 A环境正常运行,B环境就报错,找开发定位又花费很多时间。究其原因,大多数是因为数据库没有类似代码那样的版本控制自动化工具,导致管理混乱。
Flyway 这个数据库迁移库就是要解决这个问题的,可以帮我们根据数据库当前的状态,区分哪些脚本执行过,哪些没有执行,然后根据目标数据库的当前状态,决定应该执行哪些数据库脚本。
方案一
将flyway 单独维护在一个项目中,上线时手动操作,版本信息统一管理
项目地址
目录结构以及命名规范
项目结构
└─ibeautypass-ly-flyway
└─src
└─main
└─resources
└─db
└─migration
├─customer (命名规范:项目名称)
│ ├─version_1_0_0 (版本号 )
│ ├─V1_0_0_202204012042_02__create.sql (版本号 )
│ └─version_1_0_1
│ ├─V1_0_0_202204112042_02__update.sql (版本号 )
└─minapp (命名规范:项目名称)
├─version_1_0_0 (版本号 )
│ ├─V1_0_0_202204012042_01__create.sql (版本号 )
└─version_1_0_1
│ ├─V1_0_0_202204112042_01__update.sql (版本号 )
![image.png](https://img-blog.csdnimg.cn/img_convert/42953389a0c06e0b5465a80938c1b352.png#clientId=u85b77ec0-71ca-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=435&id=u90f1a424&margin=[object Object]&name=image.png&originHeight=544&originWidth=499&originalType=binary&ratio=1&rotation=0&showTitle=true&size=49535&status=done&style=none&taskId=ucd2cbd3f-d75f-4033-8be8-5145feb902e&title=项目结构以及命名规范&width=399.2 “项目结构以及命名规范”)
命名规范
在定义初始化脚本时,版本号建议为V+(编号)+yyyymmddhhmm+组号(一组01,二组02)+ __(为双下划线)+create,例如
V1_0_0_202204012042_01__create.sql。
在定义变更版本时,版本号为 V+(编号)+yyyymmddhhmm+组号(一组01,二组02)+ __(为双下划线)+update名称,例如
V1_0_0_202204112042_01__update.sql。
:::info
💡 注意,flyway严格按照版本号执行
:::
其中V1_0_0_202204012042_01__create.sql(初始化脚本)为项目所有的表结构与数据
-- SKU
CREATE TABLE [dbo].[SM_Sku] (
[spu_id] varchar(15) NULL,
[sku_id] varchar(15) COLLATE Chinese_PRC_CI_AS NULL,
[delete_flag] int DEFAULT 0 NULL,
[id] int IDENTITY(1,1) NOT NULL,
[createTime] datetime2(0) NULL,
[updateTime] datetime2(0) NULL,
[createBy] varchar(30) NULL,
[updateBy] varchar(30) NULL,
CONSTRAINT [PK__SM_Sku__3213E83F92763DA0] PRIMARY KEY CLUSTERED ([id])
WITH (PAD_INDEX = OFF, STATISTICS_NORECOMPUTE = OFF, IGNORE_DUP_KEY = OFF, ALLOW_ROW_LOCKS = ON, ALLOW_PAGE_LOCKS = ON)
ON [PRIMARY]
)
ON [PRIMARY]
GO
其中V1_0_0_202204112042_01__update.sql 脚本如下
-- 历史套餐默认客户全部
update TB_Package_P set TB_Package_P.customer_scope = 0;
-- 历史套餐默认小程序不展示
update TB_Package_P set TB_Package_P.min_app_display = 1;
-- 初始化
update TB_Package_P set TB_Package_P.create_by = '000000000';
-- 初始化默认门店图片
UPDATE
TK_Entity SET Img = 'https://staticaliyun.meibangtech.com//data/门店图片.jpg'
WHERE Img IS NULL
运行方式
java -jar ibeautypass-ly-flyway-1.0-SNAPSHOT.jar
方案二
将flyway整合在项目中,随项目启动自动执行sql脚本,每个项目都各自维护sql脚本
依赖
<dependencies>
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
</dependency>
</dependencies>
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
</plugin>
</plugins>
</build>
配置
spring:
flyway:
enabled: true
# sql文件存放目录
locations: classpath:db/migration
baseline-on-migrate: true
baseline-version: 0
encoding: utf-8
# 执行顺序,生产需要改为true
out-of-order: false
# 禁止clean命令
clean-disabled: true
validate-on-migrate: true
table: miniapp_schema_version
命名规范
SQL脚本文件名必须遵循以下命名规则:V[_][description] 。
版本号的数字间以下划线( )分隔开,版本号与描述间以连续的两个下划线(_ )分隔开。如V1_0_0_202204151646__update.sql.
项目结构
SQL脚本文件放在启动类对应的resource目录的db/migration
![image.png](https://img-blog.csdnimg.cn/img_convert/0afef46a5b7077c99c79d05e68194757.png#clientId=u92651886-e750-4&crop=0&crop=0&crop=1&crop=1&from=paste&height=326&id=u2a5ffd53&margin=[object Object]&name=image.png&originHeight=326&originWidth=472&originalType=binary&ratio=1&rotation=0&showTitle=false&size=18010&status=done&style=none&taskId=u224bb092-a7a0-481d-ae25-c8e0c52233e&title=&width=472)
对比:
优点 | 缺点 | |
---|---|---|
方案一 | 解耦,所有的项目统一在一个项目里面管理 | 不同组上线时间节点不同,版本管理需要非常严格,学习成本较大 |
每次都需要单独切到这个工程执行对应分支 | ||
方案二 | 使用方便,维护成本较低 | 和项目高耦合 |
直接在jenkins构建的时候运行脚本不需要单独执行 |