关系型数据库版本控制框架——Rumba RdbVersion

导读

如何提高应用系统安装和升级过程的质量与效率?一直以来这都是困扰开发和实施人员的问题。在此之前公司中有过很多尝试,但始终没有能够很好地解决这个问题。千帆掌柜(DPOS)也遇到了相同的问题,甚至其面临的压力远远超过其它项目。Rumba RdbVersion就是在这样的背景下诞生的,其在总结前人经验的基础上,采取了一系列创新思想,最终很好地解决了千帆掌柜的数据库安装与升级问题,且在后续的版本升级中,整体情况十分平稳。

背景

对数据库进行初始化工作是一件繁琐而易错的工作。传统的做法是将所有要做的处理表达成SQL语句脚本,交由实施或运维人员执行。这种方法要求操作人员必须具有十分丰富的经验,并且操作时必须十分小心,否则可能会造成重大的损失。而且这样的方式只适合于只有一个或几个数据库的情况;当面对采用了数据库分片技术,需要同时面对几十甚至更多数据库时,这种几乎“纯手工”方式显然已经无法满足需要。

设计目标

Rumba RdbVersion的设计的首要目标就是简化上述数据库初始化过程,尽量减少其中的“人”的因素,使得实施或运维人员在进行安装和升级过程中可以简化到只需一条指令。

Rumba Rdbversion的诞生

Rumba RdbVersion诞生在千帆掌柜(DPOS)项目中,这个项目的设计目标是至少能够满足30万家门店的进销存数据的存储与访问。由于数据量巨大,采用了数据库分片技术,在实际生产环境中其中一个资源栈就包含有200个MySQL数据库,未来会通过不断增加资源栈以满足更多的门店。设计要求每次以资源栈为单位进行升级,为尽可能减少服务停机时间,要求这200个数据库每次的升级时间最多不能超过3小时。也就是说假设同时开8个并行升级进程处理,平均每个数据库的升级时间不超过8分钟。显然这已经远远超出人力所及。

为解决这个问题,设计者总结了之前的多个解决方案的优缺点后,最终开发出了Rumba RdbVersion,并在千帆项目中得以成功应用。在实际生产中多数情况下每次数据库升级都很稳定,并且实际运行时间都不超过30分钟。

Rumba RdbVersion的特性与设计思想

1.引入与执行代码版本号对齐的数据版本号

解决问题的关键是引入数据版本号,用于标识当前的数据状态。Rumba RdbVersion对此采取的策略是让数据版本号与执行代码的版本号对齐。具体说,每当应用系统发布一个新版本的时候,对应发布的执行代码(.jar、.war或其它)中已经被标记了相应版本号,与此同时该版本的执行代码与其运行所依赖的数据库中数据的版本号必须保持一致。举例说,某个应用程序的执行代码对应的发布版本是1.2,那么它只需要保证,当数据库中数据的版本号也是1.2时可以正确运行。为保证这一点,应用程序可能会在程序启动的时候检查数据版本号,如果不是1.2则将抛出异常并终止运行。具体检查的时机和发现版本不符后的处理策略取决于具体的应用系统,而Rumba RdbVersion为此提供了帮助。

数据版本号被Rumba RdbVersion记录在数据库中与数据在一起。具体的升级程序负责将数据从某个(或多个)基础版本号升级到某个目标版本号,修改数据版本号的同时完成了所有需要的升级处理。最终的升级程序由应用系统开发团队负责开发,并随正式运行程序一同发布。安装程序则被看做是一个特殊的升级程序,其基础版本号始终为“空”(或被称为“初始版本号”),也由应用系统提供开发团队提供,并随正式运行程序一同发布。典型做法是每次发布都会提供两个程序:安装程序和升级程序。前者用于全新安装系统;后者通常仅支持从有限数量的基础版本升级。

2.交付命令行形式的安装与升级程序制品

最终交付给实施与运维人员的安装与升级程序,支持以命令行形式进行调用。而且命令行的相关参数已经被Rumba RdbVersion确定,也就是说所有安装和升级程序的调用命令都是相同的。命令行调用这一点十分重要,最大的好处是可以方便地与其它运维工具集成,例如之前提到的千帆掌柜项目中,运维人员就是利用这一特性,实现程序升级过程的自动化,极大地提高了工作效率。统一的调用参数,也有利于形成通用的运维工具,便于运维经验的基类。

3.提出安装与升级程序开发规范

升级程序中的bug给系统造成的损害并不会比正式程序轻,有时甚至更加严重。通过许多起升级程序引起的质量事故中发现,原因是多方面的,其中既有开发人员在思想上重视不够的原因,也有开发人员不知道需要按照怎样的标准编写升级程序的原因。为此在Rumba RdbVersion推出的同时推出了《有状态节点安装与升级程序开发规范》,此规范提出了开发安装与升级过程中需要注意的四项指导性原则。

原则1:重要性

原则2:幂等

原则3:可中断后继续

原则4:控制性能风险

4.同时支持SQL脚本与Java语言

SQL脚本在很多时候是针对关系型数据库的升级程序的最佳选择,其形式灵活——就是一段文本;调用方便——解释执行,无需经过编译;以及语法直接明了直达本义。当然它的缺点也十分明显:由于SQL方言的存在,必须为不同的RDBMS提供不同的脚本;当程序逻辑变得复杂时仍然可能导致代码难以理解,甚至可能变得无法实现。Java语言却能够有效地弥补SQL语言的不足。而且Java语言编写升级程序还有一个好处——Java世界有更多的现成工具可以被调用,其中也包括来自正式程序中的某段处理。

Rumba RdbVersion允许开发者同时采用SQL和Java两种方式编写升级程序。通常来说建议开发者,一些简单的升级逻辑使用SQL脚本方式,仅当逻辑比较复杂或者需要依赖Java世界工具的时候才使用Java语言编写。

5.隔离的升级程序

之所以要强调升级程序的隔离性,不妨设想这样一个场景:某个应用程序中一个较早版本的升级程序中通过接口调用了某段程序A0,并在当时已经测试通过;经过一段时间后在一个较晚版本的升级程序中也调用了相同的接口,所不同的是这段程序已经发生改变A1,并且也被测试通过。现在的问题是较早版本的升级程序如果调用了A1程序,其行为将变得不可预测。要避免这种不确定性,只有采取某种隔离策略,使得当运行较早版本的升级程序是调用的仍然是A0,而再运行较晚版本的升级程序是则调用的一定是A1。

对此问题Rumba RdbVersion的解决方案是:每个升级程序都是一个被独立发布的程序;每次发布会将其依赖的所有jar包一起打包;并且严格限制其以任何RPC方式调用外部服务。

6.三阶段升级生命周期

千帆掌柜项目,平均每个数据库的升级时间被限制不能超过8分钟。可以想见随着每个数据库的数据量的持续增长,要限制数据库升级时间将变得越来越困难。

Rumba RdbVersion为此设计了三阶段的升级生命周期,具体说就是将每次升级都划分为依次运行的——prepare、perform与complete三个阶段。其中只有中间的“perform”阶段被要求必须在停止服务情况下执行,其余两个阶段则允许在不停止服务的情况下执行。当然工具在此仅仅定义了位于开发与实施运维之间的协议,当遇到具体问题时仍然需要开发人员考虑如何安排升级处理的运行阶段。

下一步发展规划

Rumba RdbVersion自从2016年7月发布1.0版本以来,目前最新发布版本是1.5。现阶段功能发展的重点被放在提供并不断改进升级工具集,使得应用系统开发人员可以更加方便快捷地写出健壮的安装与升级程序。此外,随着应用项目上的需要,支持更多的数据库系统。最初版本仅支持MySQL数据;从1.5版本开始支持Oracle数据库;并且在即将发布的1.6版本中还将提供对PostgreSQL数据库的支持。

顺便说一下,即将发布的1.6版本包含了很多重要的特性。除上述外,其中有必要特别提到两点:SQL脚本执行器被彻底重构,解决了以往发现的某些脚本写法不支持的问题;全新设计的MySQL升级工具库,提供了ALTER TABLE语句合并执行的功能,这一点提升MySQL升级程序执行性能十分重要。

了解更多

  • 发表于:
  • 原文链接http://kuaibao.qq.com/s/20180309G1420L00?refer=cp_1026
  • 腾讯「腾讯云开发者社区」是腾讯内容开放平台帐号(企鹅号)传播渠道之一,根据《腾讯内容开放平台服务协议》转载发布内容。
  • 如有侵权,请联系 cloudcommunity@tencent.com 删除。

扫码关注腾讯云开发者

领取腾讯云代金券