目录
- 1 为什么使用数据库版本控制
- 2 数据库版本管理工具选型:Flyway、Liquibase、Bytebase、阿里 DMS
- Flyway
- Liquibase
- Bytebase
- 阿里 DMS
- 3 Flyway数据库版本管理研究
- 3.1 参考资料
- 3.2 Flyway概述
- 3.3 Flyway原理
- 3.4 Flyway版本和功能
- 3.5 Flyway概念
- 3.5.1 版本迁移(Versioned migrations)
- 3.5.2 撤销迁移(Undo migrations)
- 3.5.3 可重复迁移(Repeatable migrations)
- 3.5.4 基于SQL迁移
- 3.5.5 基于Java迁移
- 3.5.6 迁移状态
- 3.6 Flyway基础命令
- 3.6.1 迁移(migrate)
- 3.6.2 清理(clean)
- 3.6.3 信息(info)
- 3.6.4 验证(validate)
- 3.6.5 撤销(undo)
- 3.6.6 基线(baseline)
- 3.6.7 修复(repair)
- 3.6.8 检查(check)
- 3.6.9 快照(snapshot)
- 3.7 Flyway使用
- 3.7.1 在命令行中使用
- 安装
- 修改配置
- 使用
- 创建基线`flyway [options] baseline`
- 查看信息`flyway [options] info`
- 验证`flyway [options] validate`
- 执行迁移`flyway [options] migrate`
- 修复`flyway [options] repair`
- 清理数据库`flyway [options] clean`
- 3.7.2 在项目中使用SpringBoot插件进行数据库版本管理
- 使用要求
- 在项目中引入flyway依赖
- 修改配置文件
- SpringBoot配置说明
- 添加迁移文件
- 启动服务
- 引入maven插件进行其他命令操作
- 注意事项
- 我可以在 Flyway 之外对数据库进行结构更改吗?
- 迁移失败后如何修复数据库?
- 3.8 Flyway支持的数据库
- 3.9 Flyway国产数据库支持扩展
- 3.10 生产应用总结
- 3.11 其他技巧
- 3.11.1 注入环境
- 3.11.2 占位符
- 默认占位符
- 脚本配置文件
- 3.12 错误汇总
- 3.12.1 错误1
- 原因
- 解决方法
- 方法1
- 方法2(当前采用)
- 方法3
- 3.12.2 错误2
- 原因
- 解决方法
- 方法1
- 方法2(当前采用)
1 为什么使用数据库版本控制
解决我们在项目中管理数据库遇到的三个难题:
- 通过工具,规范升级脚本
- 通过工具,提升部署、升级速度
- 解决现场人员执行脚本后,不知道是否正确执行的问题
在我们大多数的项目中,我们通过Git,SVN 等版本管理工具进行代码的版本管理,促使团队成员紧密合作,方便我们管理不同分支,不同环境的代码,追踪提交历史,回退版本。但是我们的数据库,一直没有进行版本管理,依赖开发人员手动管理。下图是一个简单的项目环境示意。在项目持续到一定环节的时候,就会出现很多问题我们很难理清楚,例如:
- 某台机器上的数据库处于什么状态?
- 某条脚本在某台机器上是否已应用?
- 生产中的快速修复的脚本是否已在之后的测试中应用?
- 如何设置一个新的数据库实例?
数据库版本控制工具就是帮我们解决数据库管理混乱的工具,
可以帮助我们:
- 快速创建新的数据库实例
- 始终清楚当前数据库处于什么版本状态
- 以确定是方式将数据库当前版本升级到更新的版本
2 数据库版本管理工具选型:Flyway、Liquibase、Bytebase、阿里 DMS
该章节参考文章https://zhuanlan.zhihu.com/p/559857853。
Flyway
Flyway (https://flywaydb.org)是一款开源的数据库版本管理工具,Flyway 可以独立于应用实现管理并跟踪数据库变更,支持数据库版本自动升级,并且有一套默认的规约,无需复杂的配置,Migrations 可以写成 SQL 脚本,也可以写在 Java 代码中,不仅支持 Command Line 和 Java API,还支持 Build 构建工具和 Spring Boot 等,同时在分布式环境下能够安全可靠地升级数据库,支持失败恢复等。
Liquibase
Liquibase (https://www.liquibase.org/) 是一个用于跟踪、管理和应用数据库变化的开源的数据库重构工具。它将所有数据库的变化(包括结构和数据)都保存在 changelog 文件中,便于版本控制,它的目标是提供一种数据库类型无关的解决方案,通过执行 Schema 类型的文件进行变更。
Bytebase
Bytebase(https://www.bytebase.com/)是一款开源的数据库 DevOps 工具,面向应用开发和 DBA,管理 Schema(DDL) 和数据(DML)的全生命周期。
阿里 DMS
DMS(https://www.aliyun.com/product/dms) 是一种集数据管理、结构管理、用户授权、安全审计、数据趋势、数据追踪、BI图表、性能与优化和服务器管理于一体的数据管理服务。
Flyway | Liquibase | Bytebase | 阿里DMS | |
---|---|---|---|---|
公司 | Red Gate | Liquibase | Bytebase | 阿里 |
第一个版本发布时间 | 2010 年 | 2006 年 | 2021年 | 2016年 |
产品版本 | 开源/商业版 | 开源/商业版 | 开源/商业版 | 商用 |
易用性 | Flyway CLI简单易用 | Liquibase CLI功能比较全但概念很多,学习成本相对高 | 易用的 UI界面 | 功能比较多,上手比较困难 |
可观测性 | 通过 Schema 变更记录 查看变更历史 | 较强的可观测性,通过界面直观的看到所有的变更情况 | 完备的 GUI方式,直观看到整个变更的流程和 SQL 详情 | 所有的变更都是通过工单的方式,流程清晰 |
成熟度 | 成熟 | 成熟 | 初期 | 相对成熟 |
功能侧重点 | 数据库变更管理 | 管理数据库的变更 | 面向研发工程师和DBA 提供Schema及数据变更、版本管理,审核流水线,数据查询一站式数据库开发。 | 数据资产管理 |
支持的数据库 | 支持所有国际主流关系型数据库 | 支持所有国际主流关系型数据库 支持 NoSQL 数据库,比如 mongodb | 现已支持 MySQL, PostgreSQL, TiDB, ClickHouse, Snowflake 以及 MongoDB 数据库 | 支持大部分国际主流关系型数据库 支持 NoSQL 数据库,比如 mongodb、Redis |
可以看到,四个产品工具都能实现数据库版本管理,Flyway、Liquibase和Bytebase支持开源免费,Flyway和Liquibase发布多年,成熟稳定,Bytebase虽然易用性很高,但是面世不久,可以等待其稳定发布几个版本之后再探索使用。
由于Flyway相对Liquibase简单易用,因此,本文首先探索Flyway的使用。
3 Flyway数据库版本管理研究
3.1 参考资料
本文主要根据官方文档,结合网上其他经验和自己的试用经验编写。官方文档老版结构分明,易于阅读,适合入门。新版文档内容繁多,可以在阅读老版文档后再阅读。
2023 年 2 月 2 日官方新版文档更新,已经将老版文档的内容增加到了新版文档的第一章节,现在新版文档已经更容易入门了。
实际上老版本的文档已经包含了Flyway的核心功能和原理讲解,新版本文档感觉是为了推新的Flyway Desktop版本管理迁移脚本生成工具。官方对其定义如下
Flyway Desktop在 Flyway 之上提供了一个强大的 GUI,可帮助您轻松地对数据库模式进行版本控制,并为 SQL Server、PostgreSQL 和 Oracle 数据库生成迁移脚本。 Flyway Desktop 将数据库对象的定义保存到模式模型中。您可以进行版本控制并与团队成员共享对模式模型的更改,以快速迭代新开发。
截止2023 年 1 月 31 日,可见Flyway Desktop仅支持SQL Server、PostgreSQL 和 Oracle 数据库生成迁移脚本。支持可视化的迁移脚本生成和版本控制。
3.2 Flyway概述
Flyway 是一个开源的数据库迁移工具。它强烈支持简单性和约定优于配置。
它仅基于 7 个基本命令: Migrate、 Clean、 Info、 Validate、 Undo、 Baseline和 Repair。
迁移可以用SQL (支持特定于数据库的语法(例如 PL/SQL、T-SQL 等))或Java (用于高级数据转换)编写。
它有一个命令行客户端。如果在 JVM 上,官方建议使用Java API 在应用程序启动时迁移数据库。也可以使用Maven 插件 或Gradle 插件。
如果这还不够,还有 适用于 Spring Boot、Dropwizard、Grails、Play、SBT、Ant、Griffon、Grunt、Ninja 等的插件!
因为公司的产品和项目都是基于Spring Boot开发,因此选项使用集成 Spring Boot插件的方式使用Flyway,通过简单配置,在服务启动运行前自动进行数据库迁移。
3.3 Flyway原理
最简单的场景是将Flyway指向一个空数据库时。
它将尝试定位其模式历史表。由于数据库是空的,Flyway 不会找到它而是 创建它。会创建一个默认表名为flyway_schema_history
的空表,该表将用于跟踪数据库的状态。
紧接着 Flyway 将开始扫描文件系统或应用程序的类路径以进行迁移。它们可以用 Sql 或 Java 编写。
然后根据版本号对迁移进行排序并按顺序应用:
随着每次迁移命令的应用,模式历史表(flyway_schema_history
)也会相应更新:
有了元数据和初始状态,那么Flyway是如何迁移到更新的版本的?
再次执行迁移,Flyway 将再次扫描文件系统或应用程序的类路径以进行迁移。根据模式历史表检查迁移。对版本号低于或等于标记为当前的版本的迁移进行忽略,剩余的迁移暂时挂起,扫描完成后对待应用的升级迁移按版本号排序并顺序执行。
迁移完成后,模式历史表更新:
就这样,每次需要升级数据库版本时,无论是结构定义 (DDL) 还是数据操作(DML),只需创建一个版本号高于当前版本的新迁移即可。下次 Flyway 启动时,它会找到它并相应地升级数据库。
3.4 Flyway版本和功能
Flyway分为社区版、团队版和企业版。通过下表可知,Flyway社区免费版支持的功能相对较少,仅支持Flyway API/CLI 核心功能,7大基本命令只支持6个,不支持撤销(undo)命令。
版本 | 社区版 | 团队版 | 企业版 |
---|---|---|---|
范围 | 非常适合个人开发人员或寻求基本且可靠的框架来进行版本控制和自动部署数据库更改的非商业项目 | 非常适合希望在开发和部署数据库更改期间改进协作并微调其流程的组织 | 非常适合需要具有合规性控制的安全和自动化流程来开发、测试和部署数据库更改的大中型企业 |
价格 | 免费 | $597/年 | 获取报价 |
是否开源 | 开源 | 不开源 | 不开源 |
技术支持 | 社区支持 | Redgate 支持 | Redgate 支持 |
包含内容 | Flyway API/CLI 核心功能 Flyway 桌面 GUI 6 个基本命令:Migrate、Clean、Info、Validate、Baseline 和 Repair 支持当前数据库版本 社区支持 | 在社区版基础上增加: 试运行(Dry run) 撤消迁移脚本 遴选(Cherry pick) 跳过迁移(标记为已部署) 对象级版本控制 ** 内置 Git 客户端 支持较旧的数据库版本 获得官方技术支持 ** 标记的功能仅限 SQL Server、Oracle 和 PostgreSQL。 MySQL 即将推出 | 在团队版基础上增加: 更改报告 *** 漂移检测 *** SQL 代码标准检查 ** 迁移脚本自动生成 ** 模式比较 ** 静态数据版本控制 ** 数据对比 ** 测试数据管理(克隆) 支持扩展的数据库版本集 ** 标记的功能仅限 SQL Server、Oracle 和 PostgreSQL。 MySQL 即将推出*** 标记的功能仅限SQL Server、Oracle、PostgreSQL、SQLite |
Flyway发行版本说明,截止2023年2月15日,最新发行版本为9.14.1(2023-02-01)。
3.5 Flyway概念
使用 Flyway,对数据库的所有更改都称为迁移(migrations)。迁移可以是版本化的或可重复的(versioned or repeatable)。版本化迁移有两种形式:常规和撤销(regular and undo)。
3.5.1 版本迁移(Versioned migrations)
最常见的迁移类型是版本化迁移。每个版本化的迁移都有一个版本、一个描述 和一个校验和。版本必须是唯一的。该描述纯粹是为了让您能够记住每次迁移的作用。校验和用于检测意外更改。版本化迁移仅按顺序应用一次。
版本化迁移通常用于:
- 创建/更改/删除表/索引/外键/枚举/自定义数据类型/…
- 参考数据更新
- 用户数据更正
CREATE TABLE car (
id INT(11) NOT NULL PRIMARY KEY,
license_plate VARCHAR(64) NOT NULL,
color VARCHAR(32) NOT NULL
);
ALTER TABLE owner ADD driver_license_id VARCHAR(64);
INSERT INTO brand (name) VALUES ('DeLorean');
3.5.2 撤销迁移(Undo migrations)
撤销迁移与常规版本化迁移相反。撤销迁移负责撤销具有相同版本的版本化迁移的影响。撤消迁移是可选的,不需要运行常规版本化迁移。该功能需要团队版或企业版才能使用。开源社区版不能使用。
注意!撤销迁移实际上是对应版本的迁移完整的相反操作,是为了撤消整个版本化的迁移而编写的,如果在没有 DDL 事务的情况下对数据库进行版本化迁移失败了,这时候执行撤销迁移是不一定能够成功的。需要有适当的、经过良好测试的备份和恢复策略。
例如,对应上面的版本迁移的撤销迁移应该如下:
DELETE FROM brand WHERE name='DeLorean';
ALTER TABLE owner DROP driver_license_id;
DROP TABLE car;
具体可参考https://flywaydb.org/documentation/tutorials/undo
3.5.3 可重复迁移(Repeatable migrations)
可重复迁移有描述和校验和,但没有版本。不是只运行一次,而是在每次校验和更改时(重新)应用它们。
这对于管理数据库对象非常有用,其定义可以简单地在版本控制中的单个文件中维护。
它们通常用于:
- 创建(更新)视图/过程/函数/包/…
- 批量引用数据重新插入
例如:
CREATE OR REPLACE VIEW blue_cars AS
SELECT id, license_plate FROM cars WHERE color='blue';
具体可参考https://flywaydb.org/documentation/tutorials/repeatable
3.5.4 基于SQL迁移
迁移通常是用SQL编写的。这使得入门和利用任何现有脚本、工具和技能变得容易。它使您能够访问数据库的全套功能,并且无需了解任何中间转换层。
基于 SQL 的迁移通常用于:
- DDL 更改(TABLES、VIEWS、TRIGGERS、SEQUENCES 等的 CREATE/ALTER/DROP 语句……)
- 简单的引用数据更改(引用数据表中的CRUD)
- 简单的批量数据更改(常规数据表中的 CRUD)
SQL文件命名规范
为了被 Flyway 识别,SQL 迁移必须符合以下命名模式:
文件名由以下部分组成:
前缀:V用于版本化(可配置)、 U用于撤消(可配置)和 R用于可重复迁移(可配置)
版本:带点或下划线的版本根据需要分隔多个部分(不适用于可重复迁移)
分隔符:(__两个下划线)(可配置)
描述:下划线或空格分隔单词
后缀:(.sql可配置)
3.5.5 基于Java迁移
基于 Java 的迁移非常适合所有无法使用 SQL 轻松表达的更改。
这些通常是这样的
- BLOB 和 CLOB 更改
- 高级批量数据更改(重新计算、高级格式更改……)
3.5.6 迁移状态
执行info
命令可以打印有关所有迁移的详细信息和状态信息。
状态值 | 描述 |
---|---|
Pending | 此迁移尚未应用 |
Success | 本次迁移成功 |
Ignored | 运行时忽略此迁移migrate |
Deleted | 这是一个通过修复(repair)已被标记为已删除的迁移 |
Available | 如果需要,此撤销(undo)迁移已准备好应用 |
Undone | 此版本化迁移成功但已被撤消 |
Above Target | 此迁移尚未应用并且不会应用,因为target设置为较低版本 |
Baseline | 该数据库迁移的基线(baseline) |
Below Baseline | 此迁移未应用于此数据库,因为模式历史表具有更高版本的基线baselined |
Missing | 此迁移成功,且无法解决 |
Failed (Missing) | 此迁移失败,且无法解决 |
Failed | 此迁移失败 |
Failed (Future) | 此迁移失败,其版本高于架构历史表的当前版本 |
Future | 本次迁移成功,其版本高于架构历史表的当前版本 |
Out of Order | 此迁移成功,但应用顺序不正确。重新运行整个迁移历史可能会产生不同的结果! |
Outdated | 这是一个可重复迁移过时的迁移,应该重新应用 |
Superseded | 这是可重复迁移一个过时的迁移,已被更新的迁移取代 |
3.6 Flyway基础命令
3.6.1 迁移(migrate)
将模式迁移到最新版本。如果模式历史表不存在,Flyway 将自动创建它。
迁移是 Flyway 工作流程的核心。它将扫描文件系统或您的类路径以查找可用的迁移。它将它们与已应用于数据库的迁移进行比较。如果发现任何差异,它将迁移数据库以缩小差距。
Migrate 最好在应用程序启动时执行,以避免数据库与代码预期之间的任何不兼容。
3.6.2 清理(clean)
删除配置模式中的所有对象(表、视图、过程、触发器……)。
按照 schemas 和 defaultSchema 属性指定的顺序清理模式。
Clean对开发和测试有很大的帮助。通过彻底清除配置的模式,它将有效地为您提供一个全新的开始。所有对象(表、视图、过程……)都将被删除。
不能对您的生产数据库使用!因此,默认配置是禁止清理的,如果开发测试需要使用该命令,需要设置cleanDisabled
属性为false
。
3.6.3 信息(info)
打印有关所有迁移的详细信息和状态信息。
info
命令让你知道数据库与迁移当前的状态。您可以一目了然的看到哪些迁移已经应用,哪些迁移仍在等待中,它们何时执行以及它们是否成功。
3.6.4 验证(validate)
根据在文件系统或类路径上可用的迁移来验证数据库模式历史表中已经应用的迁移信息。
验证可帮助您验证应用于数据库的迁移是否与本地可用的迁移相匹配。
这对于检测可能阻止您可靠地重新创建架构的意外更改非常有用。
执行迁移时,通过存储校验和(SQL 迁移的 CRC32)来验证工作。验证机制检查本地迁移是否仍然具有与数据库中已执行的迁移相同的校验和。
该功能只是验证数据库中的模式历史表中的迁移历史信息是否与文件系统上的迁移文件是否匹配,防止版本话迁移文件被篡改,或者文件丢失。对于未应用的迁移,也会做出错误提示。添加
-ignoreMigrationPatterns='*:pending'
配置,可忽略待应用的迁移错误提示。该命令无法校验具体的迁移脚本中的SQL是否完全正确,能否执行成功。
3.6.5 撤销(undo)
撤消最近应用的版本化迁移。该功能只有团队版和企业版可以使用,社区版不支持。
如果target指定,Flyway 将尝试以与应用顺序相反的顺序撤消版本化迁移,直到它遇到一个版本低于目标的版本,或者一个没有相应撤消迁移的版本。如果group处于活动状态,Flyway 将尝试在单个事务中撤消所有这些迁移。
如果没有要撤消的版本化迁移,则调用撤消无效。
可重复迁移没有撤消功能。在这种情况下,应修改可重复迁移以包含所需的旧状态,然后使用migrate重新应用。
重要笔记
请注意,如果您的部署中有破坏性更改(删除、删除、截断等),您应该小心。撤消迁移假定整个迁移成功,现在应该撤消。
这意味着在没有 DDL 事务的数据库上失败的版本化迁移可能需要不同的方法。这是因为迁移随时可能失败。如果您有 10 个语句,则第 1、第 5、第 7 或第 10 个可能会失败,而撤消迁移将撤消整个版本化的迁移,因此在这种情况下无济于事。
3.6.6 基线(baseline)
基线用于通过在特定版本上对现有数据库进行基线化,将 Flyway 引入现有数据库。这将导致Migrate忽略直到并包括基线版本的所有迁移。然后将照常应用较新的迁移。
基线是和基线迁移功能配合使用的,社区版不支持基线迁移,实际上这个基线在社区版中的意义不大。也就是在非空的数据库模式中第一次使用flyway,强制必须先设置基线。
在非空的数据库模式上第一次使用Flyway 进行迁移时,会提示设置基线。
执行baseline
命令,在默认模式上创建模式历史表,创建基线记录。
重置基线
当您有许多迁移时,可能需要重置基线,在团队版和企业版中可以使用基线迁移功能设置新的基线。这将允许您减少处理大量脚本的开销,其中许多脚本可能是旧的且不相关的。https://flywaydb.org/documentation/tutorials/baselineMigrations
实际上,就是把某个版本之前积累的旧的迁移SQL进行整理,汇总成一个迁移脚本。脚本命名以
B
为前缀,版本号设置为汇总的最大的版本号,比如汇总了V1.0.5之前的所有迁移,则命名为B1.0.5__description_info.sql
,在新的数据库中进行版本迁移,则直接执行B1.0.5__description_info.sql,而不会去执行V1.0.1-V1.0.5这些SQL,减小处理开销,当累积的迁移版本太多的时候,也更加方便管理。
3.6.7 修复(repair)
修复模式历史表。
修复是修复模式历史表问题的工具。它有几个主要用途:
- 移除失败的迁移条目(仅适用于不支持 DDL 事务的数据库)
- 将已应用迁移的校验和、描述和类型与可用迁移重新对齐
- 将所有丢失的迁移标记为已删除(因此,repair必须与迁移的路径相同)
3.6.8 检查(check)
check生成报告以增加对迁移的信心。社区版不支持。
3.6.9 快照(snapshot)
snapshot 将 flyway.url 中指定的数据库模式捕获到一个文件中。辅助检查的命令,社区版不支持。
这可用于生成数据库当前状态的快照以用于 check.deployedSnapshot 或拍摄构建数据库的快照以用于 check.nextSnapshot。
3.7 Flyway使用
建议简单测试,使用命令行,易于理解。项目开发中,使用SpringBoot插件,服务启动时,自动执行迁移,保证代码与数据库版本一致性。参考https://documentation.red-gate.com/fd/usage-184127227.html
3.7.1 在命令行中使用
Flyway 命令行工具是一个独立的 Flyway 发行版。它在 Windows、macOS 和 Linux 上运行,主要适用于希望从命令行迁移数据库而无需将 Flyway 集成到他们的应用程序或安装构建工具的用户。
在命令行中使用Flyway进行数据库版本迁移是最简单,最容易理解的。下面使用命令行进行简单的使用讲解。
安装
访问https://documentation.red-gate.com/fd/command-line-184127404.html,选择适合系统版本的安装包,下载后解压即可使用。
修改配置
在安装路径的/conf
路径下修改flyway.conf
文件中的配置,保存后直接生效。
配置文件配置参考
配置参数详解
我们可以在配置文件中设置默认的配置,在命令行执行命令将按照默认配置执行。也可以在命令行执行命令时增加参数,按照命令中的参数配置执行。
例如,我们修改了flyway默认连接的数据库的flyway.url
,flyway.user
和flyway.password
,驱动flyway.driver
可以不配置,flyway会根据flyway.url
中的信息自动设置默认值。
默认的SQL脚本路径配置flyway.locations=filesystem:sql
,在文件系统中,安装路径下的sql
文件夹下。
测试时,可能需要使用clean 命令清空数据库,便于测试,需要将禁用clean设置为否flyway.cleanDisabled=false
。
使用
在命令行,执行flyway [options] [command]
命令即可。
例如,执行迁移,flyway -defaultSchema=flyway-cmd-test2 migrate
。
其中-defaultSchema=flyway-cmd-test2
,指定操作的数据库模式,在MariaDB中,即操作flyway-cmd-test2
数据库
由于是在非空的数据库中第一次执行迁移,需要创建基线,会提示创建基线。
创建基线flyway [options] baseline
查看信息flyway [options] info
执行迁移前可以先查看信息,了解当前数据库和迁移的状态flyway info
。可以看到有6个版本迁移和一个可重复迁移处于待执行状态。
验证flyway [options] validate
根据在文件系统或类路径上可用的迁移来验证数据库模式历史表中已经应用的迁移信息。
如果存在以下问题,会产生错误信息:
- 发现迁移名称、类型或校验和的差异
- 已应用的版本不再在本地解析
- 存在已准备好但尚未应用的迁移版本
- 存在迁移失败的版本
执行迁移flyway [options] migrate
可以看到,执行完6个版本的迁移,当前版本是V1.0.6
执行完迁移后,查看信息,可以看到,创建了基线,6个版本迁移和1个可重复迁移都迁移成功。
修复flyway [options] repair
如果迁移执行失败,或者是修改了已经应用的版本迁移文件,或者删除了某个版本迁移文件,执行迁移命令前会自动进行验证,验证报错无法进行迁移,这时候需要使用修复命令,来解决这些问题。
例如,故意将V1.0.6版本中的SQL改错,执行迁移后,会产生错误迁移历史。
我们将V1.0.6版本迁移SQL修改正确后,执行迁移,会提示错误信息。
这时候,需要执行修复命令,删除模式历史表中的错误迁移记录,才能再次执行迁移命令。
如果我们故意将V1.0.5版本的迁移文件的版本改为V1.0.5.1,执行info命令可以看到当前数据库迁移版本为1.0.6,V1.0.5版本的文件丢失,新增的1.0.5.1版本因为版本号小于当前版本,因此忽略不执行。
执行迁移命令,也会报相应错误
执行修复后,再执行info,可以看到1.0.5版本被标记为删除,但是1.0.5.1版本依然是被忽略,我们如果要正常使用迁移,要么设置参数(-ignoreMigrationPatterns='*:ignored'
)忽略ignored
状态的迁移,要么删掉相关迁移文件。
因此,我们在使用过程中一定要注意,已应用的版本迁移文件不能修改。
清理数据库flyway [options] clean
在开发测试中,如果需要删除测试数据,可直接运行clean命令清空数据库模式中的所有对象(包括表、视图、过程、触发器等)。再次执行迁移,则可生成干净的数据库。
3.7.2 在项目中使用SpringBoot插件进行数据库版本管理
在项目开发中建议使用Flyway的SpringBoot插件,这样在运行服务的时候会自动执行迁移,保证代码和数据库版本的一致性。
使用要求
在使用Flyway进行数据库版本管理之后,对于数据库变更就只能通过Flyway进行,要注意以下事项:
- 不能直接运行SQL命令或者通过管理工具去修改公共库的表结构
- 已经发布的sql脚本不允许修改
- 建议每个开发人员都有一个专用的开发数据库,方便排除故障,确保上传到公共库的迁移脚本都是可稳定迁移的
正确的表结构调整途径:在flyway脚本配置路径下编写新的脚本,启动程序来执行变更。
这样可以获得几个很大的好处:
4. 脚本受Git版本管理控制,可以方便的找到过去的历史
5. 脚本在程序启动的时候先加载,保证代码与数据库版本一致
6. 所有表结构的历史变迁,在管理目录中根据版本号和描述信息就能很好的追溯
当每个开发人员都有一个专用的开发数据库时,数据库开发效果最好(引用官方文档,google机器翻译)
点击访问官网文档了解详细说明
虽然可以将Flyway Desktop与由多个用户共享的单个 开发数据库一起使用,但 Redgate 建议您为每个用户提供一个专用的开发数据库。
Redgate 发现共享数据库开发环境增加了人为错误,限制了团队成员进行实验的能力,并减少了开发过程早期的功能测试。
实际上社区版功能有限,只能简单进行数据库版本管理,在团队版和企业版各种额外功能的支持下,Redgate 公司都很难解决共享数据库开发环境产生的人为错误,在使用社区版的时候,更应该每个开发人员使用专用的数据库进行开发测试,验证迁移脚本没有问题后,再上传到Git,由持续集成环境去执行健康稳定的迁移。
在项目中引入flyway依赖
在pom文件中引入flyway-core
的依赖,SpringBoot的依赖管理中已经有相关版本配置,但是MySQL5.7
在Flyway8以上版本已经不支持了,所以我这里指定了7.7.3
版本。在Flyway8.2.1以上版本MySQL的的适配已经独立拆分出来了,需要额外引用flyway-mysql
的依赖。
<properties>
<!-- flyway数据库版本管理工具-->
<!-- mysql5.7适用7.7.3;8.2.1以上mysql需要引入flyway-mysql;当前springboot自动引入8.0.5,适用mysql8,MariaDB-->
<flyway.version>7.7.3</flyway.version>
</properties>
<!-- flyway数据库版本管理 -->
<dependency>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-core</artifactId>
<version>${flyway.version}</version>
</dependency>
修改配置文件
在配置文件中配置spring.flyway相关配置,框架会自动获取数据库连接配置,多数据源时会默认使用master数据库。
spring:
flyway:
encoding: UTF-8
# 禁用clean命令,生成环境一定要设置为true,否则万一执行了clean命令会清空数据库
clean-disabled: true
# 迁移脚本的位置,默认db/migration,可配置
locations: classpath:/db/migration/MySQL
# 迁移脚本中占位符的前缀,默认为'${'
placeholder-prefix: '$${'
# 迁移脚本中占位符的后缀,默认为'}'
placeholder-suffix: '}'
# 数据库中已经存在表结构数据中使用flyway需要设置为true,且版本号要比1大
baseline-on-migrate: true
SpringBoot配置说明
名称 | 描述 | 默认值 |
---|---|---|
spring.flyway.baseline-description | 应用基线时标记现有模式的说明。 | << Flyway Baseline >> |
spring.flyway.baseline-on-migrate | 迁移非空模式时是否自动调用基线。 | false |
spring.flyway.baseline-version | 执行基线时用于标记现有模式的版本。 | 1 |
spring.flyway.batch | SQL语句执行时是否批处理。需要 Flyway 团队。 | |
spring.flyway.cherry-pick | Flyway 在迁移或撤消时应考虑的迁移。当为空时,将考虑所有可用的迁移。需要 Flyway 团队。 | |
spring.flyway.clean-disabled | 是否禁用数据库清理。 | true |
spring.flyway.clean-on-validation-error | 验证错误时是否自动调用clean。 | false |
spring.flyway.connect-retries | 尝试连接到数据库时的最大重试次数。 | 0 |
spring.flyway.connect-retries-interval | 尝试连接到数据库时重试之间的最长时间。如果未指定持续时间后缀,将使用秒。 | 120s |
spring.flyway.create-schemas | Flyway 是否应尝试创建 schemas 属性中指定的模式。 | true |
spring.flyway.default-schema | Flyway 管理的默认模式名称(区分大小写)。 | |
spring.flyway.detect-encoding | 是否尝试自动检测 SQL 迁移文件编码。需要 Flyway 团队。 | |
spring.flyway.driver-class-name | JDBC 驱动程序的完全限定名称。默认情况下根据 URL 自动检测。 | |
spring.flyway.enabled | 是否开启flyway。 | true |
spring.flyway.encoding | SQL 迁移的编码。 | UTF-8 |
spring.flyway.error-overrides | 用于覆盖特定 SQL 状态和错误代码的内置错误处理规则。需要 Flyway 团队。 | |
spring.flyway.fail-on-missing-locations | 如果迁移脚本的位置不存在,是否失败。 | false |
spring.flyway.group | 应用时是否将所有待处理的迁移分组到同一个事务中。 | false |
spring.flyway.ignore-migration-patterns | 验证迁移时忽略与此逗号分隔的模式列表匹配的迁移。需要 Flyway 团队。 | |
spring.flyway.init-sqls | 获取连接后立即执行初始化连接的 SQL 语句。 | |
spring.flyway.installed-by | 架构历史记录表中记录的用户名已应用迁移。 | |
spring.flyway.jdbc-properties.* | 要传递给 JDBC 驱动程序的属性。需要 Flyway 团队。 | |
spring.flyway.kerberos-config-file | Kerberos 配置文件的路径。需要 Flyway 团队。 | |
spring.flyway.license-key | Flyway Teams 的许可证密钥。 | |
spring.flyway.locations | 迁移脚本的位置。可以包含特殊的“{vendor}”占位符以使用特定于供应商的位置。 | [classpath:db/migration] |
spring.flyway.lock-retry-count | 尝试获取锁时的最大重试次数。 | 50 |
spring.flyway.mixed | 是否允许在同一迁移中混合事务性和非事务性语句。 | false |
spring.flyway.oracle-kerberos-cache-file | Oracle Kerberos 缓存文件的路径。需要 Flyway 团队。 | |
spring.flyway.oracle-sqlplus | 是否启用对 Oracle SQL*Plus 命令的支持。需要 Flyway 团队。 | |
spring.flyway.oracle-sqlplus-warn | 遇到尚未支持的 Oracle SQL*Plus 语句时是否发出警告而不是错误。需要 Flyway 团队。 | |
spring.flyway.oracle-wallet-location | Oracle Wallet 的位置,用于自动登录数据库。需要 Flyway 团队。 | |
spring.flyway.out-of-order | 是否允许迁移乱序运行。 | false |
spring.flyway.output-query-results | Flyway 在执行迁移时是否应输出包含查询结果的表格。需要 Flyway 团队。 | |
spring.flyway.password | 要迁移的数据库的登录密码。 | |
spring.flyway.placeholder-prefix | 迁移脚本中占位符的前缀。 | ${ |
spring.flyway.placeholder-replacement | 在迁移脚本中执行占位符替换。 | true |
spring.flyway.placeholder-separator | 默认占位符的分隔符。 | : |
spring.flyway.placeholder-suffix | 迁移脚本中占位符的后缀。 | } |
spring.flyway.placeholders.[placeholder name] | 设置 sql 迁移脚本的占位符的替换值。 | |
spring.flyway.repeatable-sql-migration-prefix | 可重复 SQL 迁移的文件名前缀。 | R |
spring.flyway.schemas | Flyway 管理的方案名称(区分大小写)。 | |
spring.flyway.script-placeholder-prefix | 迁移脚本中占位符的前缀。 | FP__ |
spring.flyway.script-placeholder-suffix | 迁移脚本中占位符的后缀。 | __ |
spring.flyway.skip-default-callbacks | 是否跳过默认回调。如果为真,则仅使用自定义回调。 | false |
spring.flyway.skip-default-resolvers | 是否跳过默认解析器。如果为真,则仅使用自定义解析器。 | false |
spring.flyway.skip-executing-migrations | Flyway 是否应该跳过执行迁移的内容,只更新模式历史表。需要 Flyway 团队。 | |
spring.flyway.sql-migration-prefix | SQL 迁移的文件名前缀。 | V |
spring.flyway.sql-migration-separator | SQL 迁移的文件名分隔符。 | __ |
spring.flyway.sql-migration-suffixes | SQL 迁移的文件名后缀。 | [.sql] |
spring.flyway.sql-server-kerberos-login-file | SQL Server Kerberos 登录文件的路径。需要 Flyway 团队。 | |
spring.flyway.stream | 执行时是否流式传输 SQL 迁移。需要 Flyway 团队。 | |
spring.flyway.table | Flyway 将使用的模式历史表的名称。 | flyway_schema_history |
spring.flyway.tablespace | 创建模式历史表的表空间。使用不支持表空间的数据库时忽略。默认为 Flyway 使用的连接的默认表空间。 | |
spring.flyway.target | 应考虑迁移到的目标版本。 | |
spring.flyway.url | 要迁移的数据库的 JDBC url。如果未设置,则使用主要配置的数据源。 | |
spring.flyway.user | 要迁移的数据库的登录用户。 | |
spring.flyway.validate-migration-naming | 是否验证脚本不遵守正确命名约定的迁移和回调。 | false |
spring.flyway.validate-on-migrate | 执行迁移时是否自动调用验证。 | true |
添加迁移文件
在配置的迁移文件路径下,创建各个版本的迁移文件。
启动服务
在idea中启动服务,可以看到Flyway在连接数据库后,验证需要执行三个迁移,在迁移之前该数据库模式为空。并依次执行了三个迁移。
三个迁移成功执行
最终,服务正常运行
在对应的数据库模式中,可以看到模式历史表中的迁移历史记录
引入maven插件进行其他命令操作
由于SpringBoot插件只是在服务启动的时候根据配置验证并执行迁移,无法进行其他操作,如果想执行其他命令,可以与Flyway命令行工具或者maven插件配合使用。
在pom文件中添加flyway-maven-plugin
的插件,更新依赖后,在maven插件中即可看到Flyway的插件。点击即可执行相关命令操作。
<build>
<plugins>
<plugin>
<groupId>org.flywaydb</groupId>
<artifactId>flyway-maven-plugin</artifactId>
<version>${flyway.version}</version>
<configuration>
<url>jdbc:mysql://192.168.16.211:3306/flyway-test</url>
<user>test</user>
<password>test123456</password>
<driver>com.mysql.cj.jdbc.Driver</driver>
<schemas>flyway-test</schemas>
<locations>
<!-- 迁移的sql的位置 -->
<location>filesystem:/yqsm-boot-module-system/src/main/resources/db/migration/MySQL</location>
<!--<location>classpath:/db/migration/MySQL</location>-->
</locations>
</configuration>
</plugin>
</plugins>
</build>
注意事项
我可以在 Flyway 之外对数据库进行结构更改吗?
不能。能够依赖数据库中的元数据并进行可靠迁移的先决条件之一是所有数据库更改都由 Flyway 进行。没有例外。这种可靠性的代价是纪律。临时更改在这里没有空间,因为它们实际上会破坏您的信心。如果之前已经手动添加过,即使是添加索引这样简单的事情也可能会导致迁移失败。
迁移失败后如何修复数据库?
如果您的数据库支持 DDL 事务,Flyway 会为您完成工作。
如果您的数据库没有,请遵循以下步骤:
- 手动撤消迁移的更改
- 调用修复命令
- 修复失败的迁移
- 再试一次
3.8 Flyway支持的数据库
Flyway官方文档表明其支持市面上常见的主流关系型数据库:
- Aurora MySQL
- Aurora PostgreSQL
- Azure Synapse
- CockroachDB
- DB2
- Derby
- Firebird
- Google BigQuery
- Google Cloud Spanner (Beta)
- H2
- HSQLDB
- Informix
- MariaDB
- MySQL
- Oracle
- Percona XtraDB Cluster
- PostgreSQL
- Redshift
- SAP HANA (Including SAP HANA Cloud)
- SingleStoreDB
- Snowflake
- SQLite
- SQL Server
- Sybase ASE
- TestContainers TiDB (Titanium DB)
- TimescaleDB
- YugabyteDB
但是有个很明显的缺点,社区版只支持发布5年内的数据库版本,团队版只支持发布10年内的数据库版本。像在项目中使用MySQL5.7版本,在Flyway8以后社区版已经不再支持,只能使用老版本。当我们的产品适配多个数据库,使用不同版本时,同时使用多个flyway版本会很混乱。
3.9 Flyway国产数据库支持扩展
官方鼓励为 Flyway 贡献数据库兼容性
https://documentation.red-gate.com/fd/contributing-database-compatibility-to-flyway-184127558.html
由于我们经常使用国产数据库,对达梦、神通等国产数据库如何适配?
可以参考以下几篇文章:
-
flyway-4.2适配DM8数据库https://blog.csdn.net/babyzhongheng/article/details/115176744
-
数据管理工具flyway版本6.4.4新增达梦数据库适配https://blog.csdn.net/u012440725/article/details/124733837
3.10 生产应用总结
可以看到,仅有少数几篇关于达梦数据库的适配,适配达梦数据库,对flyway源码改动不少,而且受到flyway版本于相关国产数据库版本限制,想要实现对国产数据库的适配,难度和工作量还是很大的。
因此,我们在使用MySQL、MariaDB、Oracle、Postgresql等国际主流数据库时,可以使用flyway社区版,来规范记录我们的数据库变动,可以快速创建干净的数据库实例,可以在生产环境中快速定位当前数据库的版本状态,了解升级脚本是否正常运行。
3.11 其他技巧
3.11.1 注入环境
使用数据库时,您通常会遇到不同的环境,例如开发、测试或生产。在这些环境中的每一个中,您可能希望执行不同的迁移,这可以通过占位符
和shouldExecute
脚本配置参数来实现。参考官方文档
官方
shouldExecute
配置可能需要高版本才能支持,在我们为了使用低版本数据库而不得已降低flyway版本时,可能不支持该方法。我认为既然是不同环境使用不同的迁移脚本,直接不同环境配置不同的脚本路径即可,直接通过不同的路径提供相应环境的脚本,简单易用。
3.11.2 占位符
可通过环境变量、配置参数或者api设置占位符的值。
默认占位符
Flyway 还提供默认占位符,其值会自动填充:
${flyway:defaultSchema}= Flyway 的默认模式
${flyway:user}= Flyway 将用于连接数据库的用户
${flyway:database}= 来自连接 url 的数据库名称
${flyway:timestamp}= Flyway解析脚本的时间,格式为’yyyy-MM-dd HH:mm:ss’
${flyway:filename}= 当前脚本的文件名
${flyway:workingDirectory}= 由“user.dir”系统属性定义的用户工作目录
${flyway:table}= Flyway 模式历史表的名称
脚本配置文件
可以在每个脚本的基础上配置 SQL 迁移。
这是通过在与迁移相同的文件夹中创建脚本配置文件来实现的。 脚本配置文件名必须与迁移文件名匹配,并添加.conf 后缀。 脚本配置文件不需要在主配置或命令行中明确列出。
例如,一个迁移文件sql/V2__my_script.sql会有一个脚本配置文件sql/V2__my_script.sql.conf。
脚本配置文件具有来自其他配置 Flyway 方式的选项的子集(例如flyway.conf)。
3.12 错误汇总
3.12.1 错误1
Flyway Teams Edition or MySQL upgrade required: MySQL 5.7 is no longer supported by Flyway Community Edition, but still supported by Flyway Teams Edition.
org.springframework.beans.factory.BeanCreationException: Error creating bean with name 'flywayInitializer' defined in class path resource [org/springframework/boot/autoconfigure/flyway/FlywayAutoConfiguration$FlywayConfiguration.class]: Invocation of init method failed; nested exception is org.flywaydb.core.internal.license.FlywayEditionUpgradeRequiredException: Flyway Teams Edition or MySQL upgrade required: MySQL 5.7 is no longer supported by Flyway Community Edition, but still supported by Flyway Teams Edition.
原因
当前Flyway社区版(我使用的是flyway-core 8.0.5
)不再支持MySQL 5.7
,团队收费版本才支持。
解决方法
方法1
使用收费的团队版,没钱一般不采用。
方法2(当前采用)
降低flyway-core
的版本,参考文章https://zhuanlan.zhihu.com/p/543779230,指定flyway-core的版本为7.7.3即可。
方法3
提高MySQL的版本,当前使用版本(8.0.5)Flyway社区版支持MySQL 8
。
注意,Flyway 8.2.1及以后版本不再直接支持MySQL,需要单独引用flyway-mysql
插件。
3.12.2 错误2
No value provided for placeholder: ${xxx}. Check your configuration!
Caused by: org.flywaydb.core.api.FlywayException: Unable to parse statement in db/migration/MySQL/V1.0.1__init_table.sql at line 2131 col 1. See https://rd.gt/3ipi7Pm for more information: No value provided for placeholder: ${填值规则编码}. Check your configuration!
原因
没有为占位符${xxx}提供值。 检查你的配置!原因是SQL脚本中字符串中存在${填值规则编码}
,被误认为是占位符。
解决方法
方法1
将spring.flyway.placeholder-replacement
设置为false
,不替换sql脚本中的占位符。
关掉后,可能会影响功能。建议使用方法2.
方法2(当前采用)
自定义设置占位符前缀,可避免与通用的默认前缀${
引起歧义
spring.flyway.placeholder-prefix = $${