vlambda博客
学习文章列表

基于Gossip算法的分布式数据库同步实现

        开篇少不了闲扯几句,今天是五一,首先祝大家劳动节快乐。前一段时间一直忙着项目的事情,也没腾出时间写点什么。最近在在设计开发一套各个系统间数据库同步的方案,由于各个系统的数据库不一定一致,因此我们基于RPC和Gossip算法实现最终一致性。


1、前言

       首先,目前我们的业务场景比较简单,由于A系统和B系统分别拥有不同的数据。因此为了各自业务的应用延伸,需要将各自的数据同步给对方。我们假设两个系统的数据库可能都是ORACLE,也可能分别是ORACLE和MYSQL。因此,我们首先开发了一套可配置的RPC模块,其中服务端根据配置决定按照ORACLE数据库接口进行操作还是MYSQL数据库接口进行操作。


      为了将来能够扩展到更多系统间数据库同步,所以我把这个设计延伸到分布式环境下数据库之间的同步实现。一般而言,在分布式环境下涉及到实时的数据都要求强一致性。而本次设计的场景是各个系统间,每个系统其实对数据没有实时性要求不是很高,所以我们实际上实现的是最终一致性。通过选择,我们采用Gossip算法去实现最终一致性。


2、Gossip算法

      Gossip算法在P2P网络中应用比较多,而类似于P2P这种场景其实我也有遇到(后面有空讲讲我们的应用场景)。Gossip的含义是流言蜚语,因此Gossip协议是利用一种随机、带有传染性的方式,将信息传播到整个网络中,并在一定时间内,使得系统内的所有节点数据一致。因此实现了这个算法,也可以认为实现最终一致性。

    

     Gossip算法有三把斧,分别叫做 直接邮寄(Direct Mail)、反熵(Anti-entropy)和谣言传播(Rumor mongering)


      直接邮寄很简单,就是某个节点收到变化信息,将该信息通过“发布-订阅”模式发送给其他节点。在我们的场景下,当数据发生变化,系统会产生一条消息通知该节点的软件(消息会通过不同的ID进行区分),该软件会去查询数据库,然后使用这种“直接邮寄”的方式推送给其他子系统节点。这里需要注意的是,通过配置文件可以得到其他节点的信息,这部分有RPC模块设计实现,这里不展开说了。


      谣言传播是Gossip算法的关键,谣言传播的主要工作方式是:当一个节点有了新的信息后,这个节点变成活跃状态,并周期性地随机选择N个节点并发送新信息。直到所有的节点都知道该新信息。这里的N叫做fanout,因为节点之间只是交换新信息,所有大大减少了通信的负担。实际上谣言传播是一种随机算法,就类似于在socket底层端口选择的列阵模型也是基于随机算法实现的,有兴趣的可以去看看列阵模型


       假设在一个20个节点的分布式节点下,N为4。根据Gossip公式大概可以算出需要log20,其中底数为4,得出结果为2.16次。我们向上取整得到3次。如下图所示,假设红色的节点表示其已经“受到感染”,即接下来要传播信息的源头,连线表示这个初始化感染的节点能正常连接的节点(其不能连接的节点只能靠接下来感染的节点向其传播消息)。并且N等于4,我们假设4根较粗的线路,就是它第一次传播消息的线路。

      第一轮消息完成传播后,新增了4个节点会被“感染”,即这4个节点也收到了消息。这时候,总计有5个节点变成红色 。

        接下来,这5个节点分别作为谣言的传播者(每个节点)继续随机选择4个节点进行第二轮传播,这一轮一共选择20个节点(可能存在重复节点)。第三轮,每个节点继续随机选择4个节点进行随机传播,三轮下来大概选择了80多个节点进行传播(理想情况下)最终节点都可以实现同步。这里有个最关键的问题需要注意,这就是何时结束传播:我们可以通过配置得到节点的总数为20,配置节点传播的数量N=4,第一轮传播,消息里面有个字段Term代表当前的轮数。收到该消息的节点将term加1修改为2,进行第二次传播。(假设这时候自己传播第二轮,结果收到一个第一轮的信息,可以忽略不处理)。最后一轮传播将term修改为3,自此完成整个集群内节点的传播。

       在随机算法的平均下,整个节点网络下传播了80多次,对于20个节点的网络来说,大概率每个节点都会轮到一次。但是,我们假设某些节点就是没有收到(假设由于某段时间内的网络问题而收不到信息),我们还有接下来的反熵修复策略进行修复。


      所谓反熵修复,本意是通过交换数据消除混乱程度。这里就是周期性的和某些节点进行互相同步信息。一般而言有两种方式,推、拉和推拉结合的方式。我们这里使用主动推送的方式。假设,我们当前环境下有A,B,C三个系统的数据库节点需要同步,我们指定了反熵修复规则,就是A节点和下一个节点B同步,B节点和下一个节点C同步,C节点和下一个节点A同步。

    

      如下图所示,假设A节点有bob和dod两个数据,通过推的方式给B节点。B节点收到A节点同步的数据将和B节点进行一一对比,比如B节点不存在bob数据,则将bob数据插入到自己的数据库中。由于是周期性修复,等下一个周期,B节点可以将自己的全部信息发送给C节点进行修复。由此,可以看到经过一段时间的修复,整个集群内的数据将实现一致性。

3、 总结

      本文比较粗略的介绍了一种基于Gossip实现的数据库同步方案,其实这也是Redis集群中同步的方案。本次我们的应用场景大都是固定节点网络,因此基于配置文件和上述的实现过程即可实现不同数据库间的同步。

     整体而言,该算法实现最终一致性还是比较容易的,当然了,对于动态节点的网络,其实该算法也是可以使用的,具体的实践还需要开发者根据应用场景进行合理的选择。