Halo Tech

Global Distributed Database技术架构与实现

一、概述

    数据是企业的命脉,企业在维持生产运行的同时必须建立完备的备份机制以应对不同程度不同规模的故障场景。在不同的容灾等级要求下,企业需要建立不同的容灾架构以满足IT系统RTO和RPO的要求,在数据库架构层面一般有同机房主备、同城主备、跨城主备等容灾方案。 在当前的数据库实现中,备库只能承担轻量级的查询功能,这将导致企业花费大量的成本维护搭建容灾系统,但是在正常的生产活动中却没有起到很大的作用,造成资源浪费。

    有没有一套架构可以在满足企业容灾要求的同时,能够使备份系统也能融合进生产体系?Global Distributed Database(简称GDD)应运而生。GDD是一套多主Raft集群,集群中各个节点互为主备,数据写入GDD集群的规则满足Raft要求。

二、GDD架构介绍

    如图所示,此GDD集群分为北京、上海、杭州三个节点,业务APP可以在每个节点上执行业务,节点上的GDD组件负责将本节点的数据写入到其他节点,也负责接收其他节点的数据。数据在节点之间的流动满足Raft协议的要求,当一份数据到达多数派节点的GDD组件后,才会真正写入到本节点的数据库中。

Image

    在集群内部运行着三组Raft数据流,正常情况下每一个节点都有一个Raft leader身份和两个Raft follower身份。

    上海机房发生的业务会通过GDD同步到北京节点和杭州节点,且一条数据到达一个节点之后就已经满足多数派的要求,就认为持久化到了Raft集群中。如果上海节点出现故障,那么北京机房和杭州机房会择机竞选上海节点身份的Raft主,当然根据Raft协议的安全性要求,拥有更多Raft数据或者Raft日志的节点会当选为上海身份的Raft主节点,假设杭州机房竞选成功那么杭州机房此时拥有两个Raft主身份,当然杭州机房会将上海机房产生的且未同步到北京机房的数据同步到的北京机房。

    上海的数据库机房宕机,那么上海业务APP通过Raft集群提供的能力,查询到新的上海身份的leader为杭州,那么业务APP可以直连杭州机房执行业务。在这个过程中以极小的RTO完成业务数据库的切换。不同的GDD配置定义不同的RPO级别,如果配置严谨的同步级别,在整个过程也可以保证RPO=0。

三、GDD技术实现

在GDD的实现路径中,主要有三个技术模块:

    (1)全局增量数据捕获

    (2)多主Raft 

    (3)高效apply

全局增量数据捕获

    GDD的数据库底座是HaloDB, GDD设计的先决条件也依赖于HaloDB的WAL日志的逻辑解码。我们实现了基于replica wal级别的日志解码(包括DML解码、DDL解码、序列解码等),解码结果存储为特定的Raft日志以供Raft模块进行节点间的数据传输。

Image

    GDD模块在实现上是一个插件模块(兼容PostgreSQL),在增量数据捕获也就是CDC模块,有两种组建模式,一是GDD模块运行于业务库直接进行wal解码,二是GDD单独运行于解码库,前者可以节省资源减小IT开支,后者可以剥离业务库运行环境,减小对业务的影响,实现业务库的高TPS需求。

    在解码方面,采用了FPI持续构建技术, 使得总能完整获取一个wal record所做的操作内容,这样实现了基于replica wal级别的日志解码。另外现有的CDC模块中,比如test_decoding、wal2json、pgoutput,基本采用单库级别的CDC捕获,在GDD中实现了一次wal读取多库表数据解码的全局增量数据捕获方法。

多主Raft

    在Raft体系中,一个节点作为leader需要主动发起对其他节点的连接,用来发送leader节点产生的Raft日志。每一个follower节点应具备一个健康检查进程,实时监控与leader节点的心跳信息,在竞选超时的瞬间发起竞选。

Image

    在多主Raft体系中,一个节点有多重身份,在如上图的Raft结构中,每个节点有一个leader身份和两个follower身份,同一个节点的两个follower身份共用一个健康检查进程。

Image

    当发生节点故障后会引起follower节点的竞选,如图所示上海节点宕机后,杭州节点竞选成功。那么杭州节点就有两个leader身份和一个follower身份。

    在传统的Raft架构中如果发生leader转移,那么业务会发生在新的leader中,并且业务数据会修改同一套Raft日志。在多主Raft体系中,每一个leader都有一套独立的Raft日志。当上海身份的leader转移到杭州节点时,上海业务也联动的转移到了杭州节点,那么上海业务所产生的Raft日志也会跟随杭州Raft日志在集群中流动。也就是说当一个Raft leader发生转移之后,这个leader身份的Raft日志不会写入新的业务数据,这个设计极大的简化了多主Raft架构的设计复杂度。

    另外由于网络的不稳定性,可能由于网络抖动导致上海身份的leader被转移到了杭州,这将导致一个奇怪的现象,Raft leader转移到了杭州,但是上海节点的数据库还在提供服务。多主Raft集群对这种情况进行了优化,即在每个节点的健康检查逻辑中添加了特殊优化,如果本节点不是本节点身份的leader,那么本节点会进行复杂的Raft日志的判断,来决定本节点是否发起抢夺leader的行为。如果是由于网络抖动导致的leader转移,在网络恢复后本节点可以轻易的抢回leader身份。

高效apply

    在所有的物理主备或者逻辑主备中都存在一个问题,就是主节点是并发写入而备节点是单进程apply,即使有些数据库做了并行apply,但其结果也不尽如人意。在物理主备上,当apply速度低于生产速度,那么除了备库无法提供最新读数据以外,这套系统迟早会写满磁盘空间。因此在数据流动下游的apply效率是一个极为严重的问题。

    由于之前提到的全局增量数据捕获技术,下游节点获取到的Raft日志是多db混杂的,我们可以选择绝对数据一致性的顺序apply,也可以按照不同的db作为切分点完成第一轮数据筛选和分发,尽可能的执行并行apply。

Image

当前GDD设计和实现了三种同步机制:

1. 批量apply模式

    GDD的apply流水线积攒批量的SQL,组成一个较大的SQL组,发给数据库作为一个整体的事务进行apply操作。用户可以自由调整批量的时间属性和批次属性,以测试在用户生产环境的最佳配置。

2.pipeline apply模式

Image

    pipeline apply模式采用libpq的异步执行模式,我们在GDD端维护PLAN列表,对一批需要同步的数据以PBE的格式做成数据包写入libpq, 统一进行返回结果集处理。这种模式下减少网络传输size, 减小网络IO等待。以提升apply的效率。pipeline模式比批量模式有40%以上的性能提升。

3.快速同步模式

Image

    如果apply速度依旧无法追赶生产速度导致有严重积压,那么可以开启快速同步模式,这种模式会通过特殊的分发机制,将前后有关联的事务分发到同一个流水线,将无关联的事务尽量均衡的分发到不同的流水线,通过并行处理可以达到倍数级的apply效率提升。

    但是快速同步模式只关心在执行顺序上两个事务是否有关联,不关心在业务逻辑上两个事务是否有关联,所以快速同步模式会打乱事务执行的顺序,可能会有业务视图不一致的现象。可以根据业务数据一致性的要求选择性的开启此模式。

四、GDD事务级别

    在Raft要求中,一个数据到达多数派节点之后才能写入Raft集群,在精密的算法要求和跨城网络延时的现实条件下,达到苛刻的Raft条件一定会导致严重的性能衰减。因此GDD内置两种事务级别使得用户可以在数据安全和集群效率之间做出权衡。

    GDD事务级别分为local_write,remote_write。在local_write级别下本地数据库完成提交即可返回客户端数据提交成功,在remote_write模式下,只有当本事务所产生的Raft日志到达多数派节点之后,才会返回客户端事务提交成功。

Image

    如图左边是local_write事务模式的事务流动示意图,事务仅仅遵循数据库内部的事务机制即可完成事务提交动作,不需要经过GDD的处理。这种模式下当北京机房出现宕机,那么势必在集群中要丢失一部分数据。当然在北京节点恢复之后,我们可以通过其他的技术手段将丢失的数据重新写入集群。

    右边即为remote_write的严苛模式,当这个事务的Raft日志到达多数派节点之后,才会返回客户端事务提交成功。在保证数据严格一致的同时,也导致了Raft集群整体吞吐量的降低。

五、GDD故障处理

    在GDD的Raft特性使得GDD集群自带故障转移的能力,可以达到在一个节点出现故障时,自动promote一个可以提供服务的主库的效果。如果故障节点在一定时间内自主或者手动恢复,那么故障节点会发起抢夺leader的行为。如果故障节点长时间无法恢复,那么将由手动或者业务APP向GDD集群发起一条业务转移请求,在业务转移成功后那么即使故障节点恢复,那么也无法完成抢夺leader的操作。

    一个发生业务转移的故障节点,由于多方面的原因需要执行节点重建操作,才可重新加入集群。重建重新加入集群后,故障节点可以再次抢夺其leader身份。业务APP再次执行业务转移请求之后,就可以将业务安全的转移回原来的节点。

六、后记

    经过团队长时间精细严谨的开发,整个项目已经进入测试收尾阶段,相信很快就可以和大家见面。由于deadline的因素也做了很多妥协,比如我们可以实现故障节点不需要做重建,就可以通过精细的数据剔除重新融入到集群中;再比如由于Raft算法复杂的扩缩容机制,使得我们暂时延缓了对GDD扩缩容功能的开发。在整个项目中也尝试了很多新的技术,遇到了很多挑战,本篇的技术分享到此为止,更多的实现细节我们江湖再见。HaloDB GDD开发团队,期待与您的下一次相遇。