分享

gossip:借助流言蜚语实现数据一致性

 古明地觉O_o 2022-12-08 发布于北京

楔子

在上一篇文章中我们介绍了 CAP 理论和 BASE 理论,其中 BASE 理论广泛应用在大型互联网的后台当中,它的核心思想就是基本可用最终一致性。但是问题来了,数据如何才能达成最终一致呢?而这就是本文的主题,通过 gossip 协议实现数据的最终一致。


什么是 gossip 协议

gossip 翻译过来是谣言,所以该协议的特点也显而易见,就是利用一种随机并带有传染性的方式,将信息传播到网络中。最终像流言蜚语一样,一传十十传百,经过一定时间后,使得集群内所有节点的数据达成一致。

可能有人觉得这种方式无法保证可靠性啊,是的,gossip 协议不保证可靠性。它的行为类似 COVID-19,就是不断地传播,直到所有人都感染为止。

而 gossip 实现最终一致性主要有三种方式:

  • 直接邮寄(Direct Mail);

  • 反熵(Anti-entropy);

  • 和谣言传播(Rumor mongering);

下面我们来分别解释这三种方式。

直接邮寄


首先是直接邮寄,它比较简单,就是直接发送更新数据,当数据发送失败时,将数据缓存下来,然后重传。

直接邮寄实现起来比较容易,数据同步也很及时,但可能会因为缓存队列满了而丢数据。也就是说,只采用直接邮寄是无法实现最终一致性的。

反熵

反熵是一种通过异步修复实现最终一致性的方法,它指的是集群中的节点,每隔一段时间就随机选择某个其它节点,然后通过互相交换自己的所有数据来消除两者之间的差异,实现数据的最终一致性。

gossip 协议的反熵每次是随机选择,但实际生产上也可以不这么做。

常见的最终一致性系统(比如 Cassandra),都实现了反熵功能。

假设集群中的 A 节点选择了 D 节点,然后两者交换数据,实现反熵。而实现方式有三种:

  • 推模式:将自己的所有副本数据推给对方,修复对方副本的熵;

  • 拉模式:拉取对方的所有副本数据,修复自己副本的熵;

  • 推拉模式:推和拉的两者结合,同时修复自己副本和对方副本的熵;

也许有很多人会觉得反熵是一个很奇怪的名词(个人觉得很酷),但其实很好理解。首先熵这个概念我们在化学课上学过,它表示物体的混乱程度,一个孤立的体系总是沿着熵增大的方向。

而对于当前而言,熵则代表两个节点之间的数据差异,差异越大则熵越大。所以反熵就是采取一些措施,来消除节点之间的数据差异,降低熵值。否则的话,节点之间的数据差异就会越来越大,因为自然情况下总是沿着熵增大的方向。

实验表明,反熵虽然可靠,但因为需要节点之间两两交换和对比所有的副本数据,所以执行反熵时通讯成本会很高,导致传播更新的速度比直接邮寄慢得多。因此不建议在实际场景中频繁执行反熵,而是可以通过引入校验和(Checksum)等机制,降低需要对比的数据量和通讯消息等。

另外执行反熵的时候还需要满足一个条件,就是相关的节点都是已知的,而且节点数量不能太多。如果在一个动态变化或节点数比较多的分布式环境(比如在 DevOps 环境中检测节点故障,并动态维护集群节点状态),这时反熵就不适用了。那么当面临这个情况要怎样实现最终一致性呢?答案就是谣言传播

谣言传播


很好理解,它指的是当一个节点有了新数据后,这个节点变成活跃状态(被感染了),并周期性地向其它节点(还未被感染)发送新数据,将其感染。然后其它节点再向其它节点发送,直到所有的节点都存储了新数据。

比如小 A 和小 B 在一起吃饭,碰巧被小 C 看到了,然后小 C 告诉小 D 了,小 D 再告诉别人。就这样到四处传播,到最后大家都知道了,小 A 和小 B 在一起吃饭这件事。

所以反熵和谣言传播之间的区别就是,前者需要传递所有的数据,而后者只需要传递新数据,而且一个人可以同时传递给多个人。

但需要注意的是,谣言传播有以下几个特点:

  • gossip 是周期性的散播消息;

  • 被感染节点随机选择 k 个邻接节点散播消息,其中 k 被称为 fan-out(扇出能力),或者干脆理解为感染能力。它是可以调整的,如果设置为2,那么每次最多往 2 个节点散播;

  • 每次散播消息都选择尚未发送过的节点进行散播;

  • 收到消息的节点不再往发送节点散播,比如小 C 将谣言传给小 D,那么小 D 在散播的时候,不会再传给小 C;

可以看到谣言传播非常适合动态变化的分布式系统,那么它的优缺点是什么呢?

优点:

1)可扩展性:扩展性极好,允许节点的任意增加和减少,新增加的节点的状态最终会与其他节点一致,并且只需要 O(logN) 轮就可以将信息传播到所有的节点这就允许集群管理的节点规模能横向扩展到几千几万个,而集群内的消息通信成本却不会增加很多。

2)容错能力:网络中任何节点的宕机和重启都不会影响消息的传播,具有天然的分布式系统容错特性。

3)去中心化:不要求任何中心节点,所有节点都是对等的,并且节点无需知道整个网络状况,只要网络是连通的,任意一个节点都可以把消息散播到全网。所以这就使得任何节点出现问题都不会阻止其它节点继续发送消息,节点可以随时加入或离开,而不会影响系统的整体服务质量。

4)一致性收敛:消息会以一传十、十传百一样的指数级速度在网络中快速传播,因此系统状态的不一致可以在很快的时间内收敛到一致,消息传播速度达到了 logN 级别。

缺点:

1)消息延迟:节点随机向少数几个节点发送消息,消息最终是通过多个轮次的传播而到达全网,不可避免会造成消息延迟。不适合于对实时性要求较高的场景。

2)消息冗余:节点会定期随机选择周围节点发送消息,而收到消息的节点也会重复该步骤,因此就不可避免的存在消息重复发送给同一节点的情况,造成了消息的冗余,同时也增加了收到消息的节点的处理压力。比如小 C 将消息告诉了小 D,虽然小 D 不会再告诉小 C,但它告诉小 E 之后,小 E 可能会再告诉小 C。因此小 C 就出现了消息冗余,它要额外处理已经知道的消息。

3)拜占庭将军问题:如果有恶意传播消息的节点,Gossip协议的分布式系统就会出问题。当然如果不是做区块链的话,那么拜占庭将军问题不需要太担心,因为我们平时使用的分布式系统,节点之间都是可信赖的,也就是说可能会丢失消息,或者有消息重复,但不存在错误消息,或者伪造消息的情况。但要是在数字货币的区块链当中的话,则必须要考虑这一点,因为除了存在故障行为,还存在恶意行为,所以必须使用拜占庭容错算法。

拜占庭将军问题描述的是最困难的、也是最复杂的一种分布式故障场景。

通过对比优缺点不难看出,达到一致性耗费的时间网络传播中的消息冗余量这两个缺点存在一定对立,如果要改善其中一个,就会恶化另外一个。因此 gossip 设计出了可以互补的反熵谣言传播两种方式。

其中反熵我们已经说过了,它的意思就是反混乱,以提升各个节点数据之间的相似度为目标。所以在反熵模式下,会同步节点的全部数据,以消除节点之间的差异,目标是让所有节点的数据完全一致。但它要求节点数量都是已知的,而且节点数量不能过多,否则会使得整个网络中消息的数量变得非常庞大,给网络带来巨大的传输开销。

因此反熵不能够频繁执行,于是就有了谣言传播。谣言传播仅仅发送新到达节点的数据,也就是只对外发送变更信息,这样消息数据量将显著缩减,网络开销也相对较小。并且谣言传播不需要节点都是已知的,正如两个小情侣也不认识全校所有的人,但如果两人偷偷接吻,被一个人看到了,很可能就全校都知道了。

通过反熵谣言传播互相搭配,就能以最小的代价实现节点之间的数据一致。


小结

  • 作为一种异步修复、实现最终一致性的协议,反熵在存储组件中应用广泛,比如 Dynamo, InfluxDB, Cassandra。建议彻底掌握反熵的实现方法,在后续工作中,需要实现最终一致性时,优先考虑反熵。

  • 因为谣言传播具有传染性,一个节点传给了另一个节点,另一个节点又将充当传播者,传染给其他节点,所以非常适合动态变化的分布式系统,比如 Cassandra 就采用这种方式动态管理集群节点状态。

在实际场景中,实现数据副本的最终一致性时,一般而言,直接邮寄的方式是一定要实现的,因为不需要做一致性对比,只是通过发送更新数据或缓存重传,来修复数据的不一致,性能损耗低。

如果在存储组件中,节点都是已知的(并且不多),一般采用反熵修复数据副本的一致性。而且在选择节点的时候,不一定非要按照 gossip 协议中说的随机选择,可以按照固定的顺序,比如修到最后可以形成一个闭环。

另外使用反熵实现最终一致性时,需要通过一致性检测发现数据副本的差异,而这是比较消耗性能的。因此可以考虑通过记录节点数据是否变化,来进行增量对比。

但如果节点数量是变化的,或者节点数量很多时,就需要采用谣言传播的方式,同步更新数据,实现最终一致(当然此时反熵也是需要有的,但不要频繁执行)。

最后有一个网站,能够以动画的形式模拟 gossip 协议的同步过程。

https://flopezluis./gossip-simulator/

可以点进去玩一玩。


本文参考自:

  • 极客时间韩建《分布式协议与算法实战》

  • https://www.jianshu.com/p/ba1415a636a5

  • https://zhuanlan.zhihu.com/p/457098784

  • https://www.jianshu.com/p/37231c0455a9

    转藏 分享 献花(0

    0条评论

    发表

    请遵守用户 评论公约

    类似文章 更多