作者:AcidGo
这是一篇比较早的实验记录文章,实验完成在2020年,部分信息跟现在的版本可能有所冲突,大家简单参考即可。
背景
相信很多数据库相关的从业人员在近些年来都有一个共识,那就是市场中国产数据库开始逐渐成为了一个焦点话题,以节约成本为目的的中小型企业和将去 IOE 作为尝试的传统金融行业纷纷开始将目光转向了国产数据库。这类新兴的数据库产品中,不乏有大数据型的、在线交易型的、离线分析型的、流式计算型的,等等,但在当下信息数据膨胀的时代,分布式特性则是众多数据库中最为亮眼的类型,例如有 TiDB、OceanBase、SequoiaDB、TDSQL、GoldenDB 等数据库。其中,巨杉数据库 (SequoiaDB) 则是众多分布式数据库的一个佼佼者,作为一款开源的金融级分布式关系型数据库,在非结构化数据处理和多类型数据库服务实例支持上,都具有出色的表现。
不过,对于传统数据中心而言,这些数据库的分布式思想理念却并不能平滑地与传统的理念相结合,例如过去的竖井式思想与当下的大集群治理所带来的观念冲突等,新的技术也开始让人们重新思考起了传统数据库与分布式数据库的差异,开始重新审视机房容灾功能。像传统金融行业使用的集中式数据库方案中,Oracle 的RAC+ADG
是一个非常流行的方案,这也让平时习惯了同城双机房的 DBA 们在初次听到两地三中心、三地五中心的数据库部署架构时会感到无比差异。而这种同城双机房的机房规划,在多数派副本的治理理念下,所提供的真正的机房级容灾却非常”虚假“,这里将巨杉数据库为例,通过剖析官方提供的双机房容灾切换方案,来加深对分布式数据库的容灾认识。
原理介绍
巨杉数据库(SequoiaDB,以下简称 SDB)可通过提供不同数据库实例服务形式来提供结构化数据的存储,也能通过原生文档操作类型API接口
、S3协议
、PosixFS Fuse
来存取半结构化数据,而底层数据存储层则是由其自研的跨主机的分布式数据库存储引擎实现。
存储集群中的每一个节点为一个独立进程,节点之间采用TCP/IP
协议进行通讯,对于集群中的节点又可以根据角色划分为编目节点(Catalog
)、数据节点(Data
)、协调节点(Coord
)。编目节点主要是存储整个集群的节点信息、用户信息、分区信息以及对象定义等元数据,大体的作用可以类比理解为 TiDB 的PD
或者是微服务中的注册配置中心;数据节点负责存储真实数据,根据特定的分片、哈希规则等来存取数据块;协调节点不存储任何用户数据,仅作为外部访问的接入与请求分发节点,协调节点将用户请求分发至相应的数据节点,最终合并数据节点的结果应答对外进行响应。
SDB 的存储引擎能够支持分布式架构的最主要原因,是将一批编目节点和数据节点划分为一个个”复制组“,并在组内使用Raft
一致性算法来达成最终一致性。复制组是 SDB 内部一个关于节点集合的逻辑概念,通过一致性算法保证组内的每个节点的数据保持一致,在发生节点故障的时候,可以在多数派可用的前提下继续提供整个组的可用性。其实,SDB 的复制组与 MongoDB 的复制集(ReplicatSet
) 非常类似,主要的目的都是使用一致性算法来实现高可用。
这种神奇的一致性算法是目前分布式数据库对share nothing
架构的一种常用解决方案,其中最经典的就是Paxos
算法了,但是 Paxos
从诞生开始就非常晦涩难懂,在工程实现上更是异常艰难,因此人们开始结合实际场景对该算法进行演进设计,包括有Basic-Paxos
、Mulit-Paxos
、Raft、Multi-Raft
、EPaxos
等算法,这些派生的实现方式都在不同的工程实践里得到优异的表现,例如Multi-Paxos
Basic-Paxo
基础上支持对多个提议进行共识判断,而Raft
假定了日志的单调顺序应用和更加严格的选主条件以降低算法的复杂度。但以上的算法在达成共识的策略上都是一致的,需要保证集群内的多数派成员对提议/日志达成共识并应用,简单来讲就是3个节点的集群可以允许1个节点不可用,而剩下的2个节点可以作为多数派成员继续决议来达成共识,其成员数量的经验公式则是大于1的奇数,这也是为何 zookeeper、etcd、MGR 等都是建议部署3个节点的原因,但是也不能太多,因为节点越多,达成共识的信息沟通成本也越大。关于这种共识成本的表现,最为明显的就是蚂蚁金服的 OceanBase 数据库的部署,为了满足异地容灾,支付宝甚至使用了三地五中心的容灾架构。
在了解了 SDB 中关键的一致性共识算法之后,重新思考这其中的困境——Raft
自身可以容忍3个节点里有1个节点故障,但是对于大多数同城双机房的环境而言,当主机房的2个节点发生故障时,备机房的1个节点并不能保证集群可用,最多也只能保证可读,而无法通过多数派决议来应用日志。对于传统金融行业而言,在相关监管要求下,同城灾备机房和异地容灾机房在 Oracle、DB2 等数据库产品上都能有满足的解决方案,所以当其开始转向尝试例如 SDB 这种分布式数据库时就会开始发现,副本的日志同步需要每个机房间都有高带宽、低延迟的网络要求,并且因为副本的数据量是一致的,每个机房的节点规模都需要保证有一份完整的副本。面对这些新架构下的机房要求,部分企业初期是选择了同城双机房的规模来进行部署,暂时忽略了“多数派机房”的困境,而数据库厂家也相应地推出了适合双机房架构的方案,例如巨杉数据库的手动容灾切换、OceanBase 的双机房两集群间 OMS
同步等。
这里通过研究 SDB 在主机房节点不可用时如何通过切换来进行容灾的,来加深对 SDB 的理解,同时也针对性地评估一下这种“巧妙”绕过多数派算法的方法存在哪些优劣。
切换流程
首先,以官方提供的 3.4 版本作为操作,分析该切换工具的原理。对此工具的使用可见巨杉官网(http://doc.sequoiadb.com/cn/sequoiadb-cat_id-1561381816-edition_id-304)。
工具的执行文件都存放在 sequoiadb 安装目录下的 tools/dr_ha 目录下,目录结构如下。init.sh
根据配置信息对集群做初始规整。split.sh
根据配置信息对集群做拆分。merge.sh
根据配置信息对集群做合并,大部分是为了合并为拆分前的大集群。cluster_opr.js
是真实的操作脚本,以上都是对该脚本的封装。
假设目前的集群环境如下,是一个最精简的双机房多主机部署规模,其 SUB1
是主机房的两组节点SUB2
是备机房的一组节点。目的是实现让SUB2
中的一组节点来提供读写功能,摆脱多数派成员失效时无法写的问题。使用
dr_ha
工具实现这个切换流程时,主要涉及有三个阶段,分别是切换前的集群刷新(init
)、将打打大集群分裂为两个单独的集群并仅激活备机房的集群(split
)、将两个拆分的集群合并回原来的集群(merge
)。这几个操作实际都是由cluster_opr.js
这个脚本来完成,对此脚本进行分析,梳理相关流程的执行步骤。当备机房 SUB2
执行了 split
提升为单独的可用集群后,整体的应用操作读写如下。init
阶段的逻辑流程如下。init
阶段的主要功能函数调用如下。init
阶段的调整操作比较少,大部分都是检查前置条件和配置参数,并且将第一次连接可用协调节点获得的集群内复制组信息保存在 init 文件内,以保证后续操作的集群信息提取都是基于这一份快照,如果配置里选择需要同步这份文件时,执行节点会通过 SSH
的方式将文件拷贝至对应主机。整个过程对集群有调整的一点就是在建议重新选主时,更新复制组的权重,让复制组内的主节点重新选举出来,这一步可以将集群主节点计划内地切换到分裂后的可用子网。split
阶段的逻辑流程如下。split
阶段的主要功能函数调用如下。split
阶段的操作目的是通过修改所有编目节点中的复制组信息,将不在激活子网内的主机的复制组都从集群中剔除,同时将非激活子网节点从集群的编目节点服务地址中剔除,然后重启保留可用的主机上的所有节点。在进行核心操作之前,需要将待操作的编目节点关闭再以 standalone
模式启动,让编目节点单独接受客户端连接来执行后续的配置更新动作。当所有的配置更新都完成后,执行主机对操作主机进行节点重启以生效所有配置并且推出独立模式。restartAllHostNode
函数内部通过一定的等待和检查机制来确保所有节点都正常重启完成。当节点都重启完毕,设置激活的保留子网将蜕变为一组节点的方式提供服务,而另外一组多数派由于被设置了只读模式,因此不能接收写请求。merge
阶段的逻辑流程如下。merge
阶段的主要功能函数调用如下。merge
阶段的操作可以看成是使用 init
阶段保留的集群配置快照来回退 split
阶段的操作,使用与 split
相同的流程把完整集群的配置刷回到所有节点,然后再经过一次节点重启后,分离的两个子网将合并为一个大的集群继续提供服务。
切换测试
在理解了切换脚本的操作员落后,通过多个场景模拟验证此方案的操作性、可行性和稳定性。
以下为 dr_ha
工具集中涉及切换动作相关的配置参数列表。除此以外,脚本内部还有一些预先定义或通过可配参数而自动生成的内部参数,列表如下。
模拟验证的环境使用如下的三台
CentOS 7
的虚拟机,上面部署了官方可下载的 3.4 版本。设计的集群内各角色复制组如下规划,并且所有节点都不会开机自启动(后面会解释此原因)。
为了构造参考数据,以下分别在 SDB 集群中创建一个 API 操作集合和 MySQL 实例,方便在切换中实时插入以判断切换过程中的服务连续性。
MySQL 服务实例主要是方便查看服务连续性的时间信息,其库表结构如下。API 创建的测试集合主要是为了捕获更原生、更贴近 SDB 底层报错码的信息(http://doc.sequoiadb.com/cn/sequoiadb-cat_id-1432190985-edition_id-304),其创建如下如下。
在虚拟机环境,此处使用 HAProxy 来代替传统数据中心的 F5 等负载均衡设备,分别反代三个协调节点的 11810 端口(TCP)和 MySQL 实例的监听端口
3306 (TCP)
。使用 192.168.66.130
作为代理主机,HAProxy 相关配置文件如下。注意:此配置并不适用生产环境。通过简单的
shell
命令来对两个模拟数据地址做插入,以下的命令分别放置在两个不同的 shell
脚本里,在整个测试过程中都保持持续运行状态。大部分双机房部署的大集群架构中,默认都是尽可能地将主机房内节点作为主节点,以减少路由到备机房的写求情,因此
init
阶段,通过参数将主节点刷至子网 SUB1 内。选择 sdb1
为 SUB1
上的操作主机,sdb3
作为 SUB2
上的操作主机。
在 sdb1
上备份 cluster_opr.js 后修改其可配参数如下。在
sdb3
上备份 cluster_opr.js 后修改其可配参数如下。这里
ACTIVE
参数在 init
阶段只会影响重新选举时的主节点权重,不会产生只读节点。
删除执行主机上的 datacenter_init.info
文件,根据上述参数信息执行 init
,从执行结果可见,其将会收集环境信息保存到初始文件,同时将文件通过 SSH
拷贝至所有子网的主机上。查看当前三个主机上的节点分布情况,可见目前所有的编目节点
leader
和数据节点 leader
都分布在了 SUB1
,即主数据中心,这也是将 SUB1
的 ACTIVE
设置为 true
的效果。下面将使用
dr_ha
工具集中的 split
命令,通过修改编目信息的方式来让原集群的少数派继续提供服务,首先需要修改配置参数,因为 split
中的 ACTIVE
代表指定编目节点的 datacenter
元数据是否标记为只读,因此需要在 SUB2
上的 sdb3
主机执行,并且将其 ACTIVE
设置为 true
。修改后的 sdb3
相关参数如下。将
SUB1
下的 MySQL 实例关闭和 sdb1
和 sdb2
的节点关停,然后使用修后配置参数后的 sdb3
执行 split
。完成后检查备机房的节点情况,发现确实都变为了主节点。
观察同时在操作的数据修改动作,在集群出现错误时,MySQL 插入会报错,这里提示的会有无法连接储存引擎、存储引擎报错等信息。
API 的操作会更加直观地反映出报错信息,这里分别出现了有感知到集群操作的不是主节点(
-104
)、网络无法连接(-79
)、节点处于故障状态(-250
)等信息。为了进一步验证切换结果,查看
SUB2
上的集群信息,可见其组成员均只有 sdb3
上的节点。一切符合预期后,恢复
SUB1
上的服务,并需要合并会原有的大集群,但由于 SUB1
上的节点是被分裂在另外一个子网,如果直接启动后被应用连接,那么就会产生脑裂,对此需要保证服务器启动时不会自动带起节点服务,需要手动拉起独立模式运行的编目节点,然后设置 SUB1
子网的 ACTIVE
为 false
后,在 sdb1
上执行 split
动作,目的是让 SUB1
进入只读模式,继续让备机房提供服务,避免脑裂。sdb1
的配置如下。在
sdb1
上执行 split
。这个时候
SUB2
的新数据变更是不会同步到 SUB1
上的,需要将集群合并,因为 SUB2
的 LSN
是大于 SUB1
的,所以依旧保持 SUB1
的 ACTIVE
为 false
,而 SUB2
的 ACTIVE
为 true
的配置,分别在 SUB2
的操作机 sdb3
和 SUB1
的操作机 sdb1
上执行 merge
动作。
首先在 SUB2
上进行合并。这里在多次测试中都出现了失败,根据日志推测可能是使用大集群配置生效时,编目信息匹配、大集群主节点确认等过程会导致数据节点启动后自动退出。然后在
SUB1
上进行合并。要在两个子网内执行是因为工具仅能在一个子网内操作,所以需要共同参考之前初始化的配置文件来做集群信息的“一致性”恢复。
最终检查每个节点的状态以及日志同步情况。起始经过重复以上的测试,
SUB2
的数据节点在第一次 merge 后就无法正常启动,相关报错为 SDB_CAT_AUTH_FAILED
,即编目授权错误。在经过一段时间后,确认集群正常后手动启动才能将其启动完成,最终结果如下。确认集群无错误后,启动 SUB1 上的 MySQL 服务,并查看 MySQL 实例和 API 操作集合中的数据一致性。
在 MySQL 实例的插入信息中来分析服务连续性。
手工切换
为了更加深入地还原整个切换过程中的具体操作,使用纯手工切换集群的方式来代替脚本的封装动作,从而加深对 SDB 在整个切换中的细节认识。因为 init
阶段主要是初始文件生成与分发、重选举等作用,而 merge
阶段则是使用 split
阶段类似流程来回退配置,于是这两种不详细展开,主要介绍下 split 的手工执行模式,以加深理解。
-
将当前子网的编目节点设置以独立模式启动
在正常待操作集群里,选择sdb3
来作为后续提供服务的子网,将其编目节点设置为独立模式,避免收到计划外的编目信息调整,这也是保证与初始文件信息对齐。 -
重启当前子网的所有节点
主要是通过重启来更新配置,让编目节点脱离独立模式,从而分裂出新的集群。对比当前本地的节点,判断是否都启动完成。
思考
在目前使用多数派决议的共识算法的数据产品中,客户的双机房环境是一个比较困扰厂家的问题,无法完全依靠算法特性来实现机房级别的容灾。对于这种问题,大部分厂家都是选择保持集群架构的前提下进行双机房容灾方案设计,例如部分厂家会选择在同城备机房新部署一个一比一的集群规模,然后使用日志同步工具接收并应用主机房的事务日志,类似于将整个集群当作一个 MySQL 实例来规划一个主从复制。这次研究的巨杉数据库则是另辟蹊径,选择将集群分割来提供容灾“切换”,通过上述的验证实验,发现此功能确实可以满足,但也仅是在非常简单的应用模型下得出来的结论。从总的体验上思考这种方式的方方面面,以鄙人浅见得出如下点点。
-
主机的
SSH
密码和节点的密码必须一致
这一点不是硬性的限制,而是脚本配置中仅提供统一用户和密码的注入,如果将其配置清单详细展开,那么需要配置的事项将非常繁琐。但是,大部分生产环境的 SSH 口令都是接入堡垒机的,随机密码串也都是不一致的,况且将明文密码直接写入到配置文件内也违背安全规范,所以在真实的生产环境下,参数的注入并没有测试场景里那么轻松。 -
执行过程中没有异常清理和故障回退方案
操作脚本是一个js
文件,内部对于报错过程都是以过程式编程的思想将RC
返回为非真,然后退出执行,但是对这些中途退出的功能函数却没有合适的恢复方案,没有设立能够充分的预期异常应对,在操作中遇到中途退出,要么重新执行其他阶段来“回退”,要么研究代码逻辑,在完整性上此工具还不是非常成熟,甚至在 1199、1209、1218 行还有代码错误,此外还需要加强一些函数的健壮性,例如getClusterHostAddrs
函数在遇到集群有ErrNodes
信息时返回的主机列表会存在空值。 -
应景场景的实用性思考
机房级故障的发生往往会伴随非常错综复杂的故障现场,也不仅仅像某些厂家演示的单纯剪网线那么简单模拟,就以 SDB 的这个切换为例,当真正发生机房故障时,可能会有较多的网络分区和部分故障,上行应用做应急切换时也是尽最大努力将所有能切的流量切入备机房,面对这些复杂的场景,非常难以保证通过分裂大集群的方式来实现预期容灾效果,因为无法保证能够连上SUB1
子网的主机,也不能检查其已没有承载应用读写,这时候的切换动作往往带来巨大的脑裂风险。在需要合并时,如果无法保证两个子网的数据节点的冲突情况,故障清理和合并回大集群则是一件头疼的事情,甚至会带来二次故障。 -
初始配置文件的时效性
init
阶段生成的初始配置文件是整个过程中非常关键的一个参考点,为了保证分裂时应用的配置有效,则要确保参考文件的时效性,那么是需要每天或每小时生成一次文件,还是调整过组信息后人为收集一次呢?如果使用了一份“过时”且有差异的配置文件,那么执行分裂和合并动作都会夹杂着不稳定性。 -
共识算法架构的分布式数据库双机房高可用的困境
在了解了主流的多数派共识算法后,大家也清楚为了实现哪种层面的容灾,就至少需要把这种层面的整体资源当作一个可用区(Active Zone
),而双机房在机房级容灾场景中只有两个可用区,本身的模式也是与算法原则有出入的。SDB 的集群分裂方式利用了自身管控、数据、计算等节点分离的架构,通过修改编目信息来应对这个问题,确实提供了很不错的解决思路和实践,也希望最近的 5.0 版本可以更加关注机房容灾的场景,提供更加健壮的数据库高可用,本人非常看好此产品的发展。小声在这里吐槽一下,希望官方可以推出Golang
版本的驱动。
这次实验的原由是因为内部最近在讨论关于分布式数据库的机房容灾而研究的,整个过程大部分都是以非专业的角度去做测试,如有错漏也欢迎指正。在了解分布式数据库的过程中,也确实感受到了近期国产数据库的突飞猛进,在此由衷希望国产数据库能再接再厉,更上一层。
原文始发于微信公众号(AcidPool):巨杉数据库双机房容灾切换剖析
版权声明:本文内容由互联网用户自发贡献,该文观点仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 举报,一经查实,本站将立刻删除。
文章由极客之音整理,本文链接:https://www.bmabk.com/index.php/post/171083.html