可靠的企业战略,数字化转型,智能化转型和企业架构智库

【数据库迁移】在大型项目中使用Flyway进行数据库迁移

“美景”的图片搜索结果

在过去的四年中,我们一直在使用Flyway在大型客户端项目中迁移我们的数据库。该项目涉及多个站点的100多人。随着时间的推移,应用程序和数据迅速增长。因此,数据库必须发展以应对应用程序的更改。我们在临时环境中迁移数据库时遇到了一些挑战。在这篇博文中,我想分享我们如何使用Flyway迁移数据库。

使用Flyway进行数据库迁移


以代码开发迁移脚本


作为启动,我们在一组JPA实体中定义域模型。我们还使用JPA注释来约束不可为空的数据类型max。然后我们使用JPA mapping-tool生成与JPA实体对应的DDL,并使用DDL设置初始数据库。这组DDL与源代码一起被检入Git存储库。当开发人员对数据库进行更改时,他必须再次生成DDL,然后创建对数据库的更改集。他可以看到Git中当前DDL与之前DDL之间差异的变化。根据差异,开发人员为数据库迁移创建了一些脚本。脚本还迁移数据库模式和现有数据。

我们在scrum中开发并在sprint结束时提供发布包(我将其命名为增量)。下图说明了不同增量的脚本。

Database migration scripts in different versions

Database migration scripts in different versions

当sprint结束时,我们的jenkins发布版本将Flyway脚本打包到新版本包中。 通常,脚本放在我们讨论过并与DevOps达成一致的目录中。 DevOps将Flyway配置为在预定义目录中扫描。 在部署新应用程序之前,他们运行Flyway来执行找到的脚本。 通过这种方式,数据库迁移以受控方式工作。

 

我们使用Flyway迁移数据库的工作流程


我们有一个经典的分期流程来促进我们增加到PROD(生产)阶段。 下图是从DEV到PROD的开发过程的简化版本。 如图所示,我们有三个阶段,DEV,TEST和PROD。

The promotion of increment from Local to PROD

 

 

The promotion of increment from Local to PROD

在sprint结束时,我们将新增量提供给最低测试环境(DEV)。 DevOps接收传递并在DEV上部署增量以进行测试。 如果测试并批准了此增量,那么他们会将其部署到TEST。 我们不会等待外出增量的测试结果,而是继续开发新版本,然后在下一个sprint中提供它。 如果在暂存环境中通过测试找到问题,我们将在同一个git分支中修复它。 根据修复,我们构建一个新的增量,并将其作为修补程序提供给同一个阶段。

使用Flyway架构历史诊断数据库


完成Flyway迁移后,我们可以在架构历史记录表中看到更改历史记录。 模式历史记录记录执行的Flyway脚本的所有重要信息。 下图显示了环境DEV,TEST和PROD的简化模式历史记录。

Flyway Schema History

Flyway Schema History

 

当您获得一个损坏的数据库并且您应该修复它时,架构历史记录中的信息可能非常有用。由于我们使用Flyway自动化数据库迁移,因此我们可以轻松找出数据库发生的情况和时间,然后快速识别不规则性。有时,如果在环境中部署了先前的修补程序,则可能会很棘手,因为架构历史记录包含额外的修补程序脚本。在下一节中,我将解释修补程序迁移的处理方法。

Flyway的修补程序


我们不时会在某个临时环境中发现问题。如上所述,我们通过修补程序修复问题。我们遇到了修补程序开发中的一些挑战。例如,一个挑战是以正确的时间顺序保持每个阶段环境的数据库模式历史。

通常,我们开始在最高阶段修复问题,然后尝试将数据库更改填充到其他分段环境。我们使用一些技术转换脚本以避免重复执行的副作用。

 

首先修复问题环境


通常,如果舞台环境中出现问题,我们会在同一个git分支上解决该问题,然后将修补程序提供给该环境。这对我们来说是最简单,最省时的方式。

假设我们在版本7的PROD上检测到数据库相关问题,那么我们使用包含一些脚本的修补程序v7.1来处理它。当版本8的应用程序增量到达PROD时,Flyway可以识别出v8中的脚本版本高于PROD上的版本7.1并且在v7.1之上执行v8的脚本。对于Flyway来说,脚本版本应该始终是升序而不是降序。

Hot fix v7.1 is deployed on PROD

Hotfix v7.1 is deployed on PROD

 

将Flyway脚本从修补程序合并回主流


在部署了修补程序v7.1之后,我们在PROD上完成了数据库更改,这在DEV和TEST上是不存在的。 我们如何使DEV和TEST DB与PROD保持一致? 简短的回答是将v7.1中的脚本合并回主流。

Merge flyway scripts from hot fix back to main stream

Merge Flyway scripts from hotfix back to main stream

 

上图显示了从修补程序v7.1到主流(v10)的合并。乍一看似乎很简单,因为我们可以挑选修补程序提交并将脚本移动到主流中。实际上,简单的复制粘贴不起作用,因为Flyway只在最高版本之上执行脚本。我们可以回想一下,TEST中数据库模式历史记录中的最高版本是版本8和DEV版本9.Mayway会注意到v7.1脚本的版本低于v8或v9,因此在DEV和TEST上都忽略它们。同样,Flyway的默认行为是按升序执行脚本。

现在让我解释一下我们如何合并脚本。

  • 首先,我们将修补程序中的脚本合并到具有更高版本号的开发分支
  • 其次,我们重写脚本,使它们可重新运行并具有幂等性。具体来说,合并的脚本能够发现是否在数据库上完成了相同的操作,然后相应地跳过它们自己。当新的增量随后部署在PROD上时,这些“新”脚本将自行跳过,因为之前已经部署了修补程序。


零停机时间使用Flyway迁移数据库


由于我们的应用程序是24×7在线,因此不允许在生产环境中停机。这意味着当我们迁移数据库时,应用程序必须保持活动状态。

 

蓝绿色部署零停机时间


我们遵循的策略是所谓的蓝绿色部署。也就是说我们在每个阶段同时维护2台服务器。下图描述了蓝绿部署的工作原理。如图所示,两台服务器共享一个数据库。

 

Blue green deployment illustration

Blue green deployment before and after

 

在部署之前,路由器将请求重新路由到具有较新版本的蓝色服务器,而具有旧版本的绿色服务器则作为备份。当新版本发布并交付到此阶段时,我们将新版本部署到绿色服务器。我们通常在部署之前迁移数据库。

在数据库上迁移并且绿色服务器上的部署完成后,我们将请求重新路由到绿色服务器。然后,此服务器开始主动提供请求,我们现在将其称为活动服务器。下次将另一个新版本发送到此阶段时,它将部署到蓝色服务器。我们一遍又一遍地重复相同的过程。

两阶段数据库迁移,以支持蓝色和绿色服务器


由于我们让蓝色和绿色服务器共享数据库,因此该数据库必须在每次迁移后支持两个服务器。这样,如果新部署的版本无效,我们可以切换回旧版本的服务器。我们对此用例应用了所谓的两阶段迁移技术。例如,假设我们要从表USER中删除列PHONE。下图说明了我们的工作:

在第一阶段,我们删除了应用程序v6中对列的引用。没有架构更改。由v6插入的记录不包含colum PHONE的值。如果我们需要切换回v5,我们会创建一个触发器,它会使PHONE列填充一些值。因此,带有v5的服务器仍然可以使用新插入的数据。
在第二阶段,我们从表USER中删除列PHONE。数据库架构更改。但它完全没问题,因为两个服务器(v7和v8)都没有列的引用。

2 phase database migration

2 phase database migration

 

具有零停机时间要求的迁移结果

  • 蓝色和绿色服务器在每个阶段共享一个数据库,而此数据库随时支持两个服务器。
  • 我们总是有一台主动服务请求的服务器,另一台服务器待命。
  • 如果新应用程序失败,我们可以将被动服务器重新联机。但我们不撤消数据库迁移。


环境特定数据库迁移


通常,我们使用相同的迁移脚本在每个环境中设置数据库。有时,这将无法正常工作,而数据库表的某些配置因环境而异。例如,我们在表CUSTOMER中有一个LOCALE列用于语言首选项。然后我们使用该列的默认值“EN”编写初始迁移脚本,这对于西方国家的环境是正确的。但对于欧洲以外的环境来说并非总是如此。在中国等非英语国家的环境中,该列的默认值应为“CN”。困境在于设计脚本以便在环境与环境之间以不同的方式工作将是一种过度的做法。

幸运的是,Flyway可以选择这种情况。我们将原始脚本复制到不同的目录中并根据目标位置进行调整,然后在中国环境中配置Flyway以在此目录中扫描脚本。这样,该目录中的迁移脚本可以根据环境正确设置LOCALE列。

此环境特定目录的解决方案使Flyway能够解析数据库模式中的本地依赖关系。但是,这种方法应该谨慎使用,因为它可能导致不同环境中的不同模式历史记录,并且模式历史记录的差异将使数据库分析变得复杂。

 

使用Flyway迁移大表


生产数据库中的一些表包含数千万条记录。那些大表使迁移比我们预期的更复杂。简而言之,迁移大型表需要更长的时间。由于每个Flyway脚本都在事务中运行,因此我们需要更大的磁盘空间来启用回滚。当我们迁移大型表时,阻止了其他访问(从应用程序)到大型表。在最坏的情况下,访问在迁移完成之前达到超时并失败。

 

迁移大表的数据


在实践中,我们将在迁移数据之前分析大表。我们总是在真实PROD数据库的副本上组织测试运行,以查看迁移需要多长时间。如果迁移到一个大型表是一个长时间运行的过程,比如超过30秒,我们只需将该脚本从Flyway中取出,进行优化并手动执行。此外,我们必须应用分而治之的原则。那就是将数据分成适当的部分,然后在提交中迁移每个部分。我们将阈值定义为30秒,因为应用程序事务在30秒后超时。

 

迁移大表的模式


触摸大桌子有点可怕,因为你害怕受到伤害。但是,我们必须更改架构,否则它无法支持应用程序更改。如果更改是数据库上的REORG挂起操作,那么我们必须对该表执行REORG。没有DBA会在PROD数据库的大表上批准这样的REORG,因为它将完全阻止对表的访问很长时间,这违反了零停机时间策略。

目前,我们正在实践一个概念来解决这个问题。我们所做的是将大表重命名为USER to USER_OLD,然后立即创建一个具有相同名称和我们想要的正确模式的新空表USER。之后,我们将旧数据从表USER_OLD递增传输到新表USER。诀窍是我们必须在单个事务中进行重命名和创建。这样就可以优雅地完成模式迁移,应用程序甚至不会注意到表已经更改。

 

总结


Flyway帮助我们以可控的方式管理所有临时环境中的复杂数据库迁移。我们开发迁移脚本并将其包含在交付包中。然后,DevOps负责暂存环境中的其余部分。虽然我们仍然需要与DBA密切合作,但开发人员不再需要负担数据库迁移的负担。如果出现数据库问题,则Flyway架构历史记录是检查数据库状态的便捷工具。

除了与Flyway的例行数据库迁移之外,由于我们项目中的挑战,以下几点是调整。

  • 合并的修补程序脚本必须是幂等的,同时将修补程序合并回主流。
  • 我们应用蓝绿部署和两阶段数据库迁移,以便应用程序具有零停机时间。
  • 分别迁移大表

 

原文:https://www.novatec-gmbh.de/en/blog/database-migration-flyway-large-project/

本文:http://jiagoushi.pro/database-migration-flyway-large-project

讨论:请加入知识星球或者微信圈子【首席架构师圈】