分布式系统如何保证一致性

分布式一致性算法概要
随着各种高并发访问、海量数据处理等应用场景越来越多,为了应对这些使用场景,分布式系统应运而生 。分布式系统得以发展,得益于诸多优点,比如:可以避免 单点故障 ,容易 横向扩展 等 。所谓单点故障指的是:单个组件发生故障会导致整个系统的瘫痪,而容易横向扩展的意思是我们可以通过增加机器来提高整个系统的性能 。分布式系统在带来诸多优点的同时,也带来了一些挑战,我们下面来重点描述清楚其中的一个核心挑战: 在分布式系统中如何保证数据的一致性  。关于分布式系统的基本概念,可以参考相关的理论书籍 。
在众多分布式一致性协议中, paxos 算法经过了严格的数学证明 。然而由于该算法非常难以理解,尤其是在各种系统的实现时,一般采用 paxos 的简化版本或者 paxos 的一些变种协议,比如 Raft 和 Zab 等 。为此,本文选取 Raft 算法来进行介绍 。我们可以通过构造一个如下的应用场景来帮助我们理解一个算法 。

分布式系统如何保证一致性

文章插图
 
以一个数据存储系统为例,如果 client 想要在 server 上设置一个值,假如一开始有一个 server 和 一个 client,此时只要一个server 完成值的设置即可 。可是如果有多个 server 存在的话,要如何保证所有的 server 节点上存储的值是一致的呢?如果各节点的初始状态一致,每个节点执行相同的操作序列,那么他们最后能得到一个一致的状态 。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个“一致性算法”以保证每个节点看到的指令一致 。
Raft 算法介绍
在一个由 Raft 协议组织的集群中有三类角色:
  • Leader(领袖)
  • Follower(群众)
  • Candidate(候选人)
每个节点都会处于以上三种角色中的一种 。
算法的主体大致可分为2个过程:
  • 选举(Leader Election)
  • 日志同步(Log Replication)
下面我们分别介绍 Raft 算法中的选举流程和日志同步流程,随后会介绍 Raft 算法对一些异常情况的处理 。
选举
选举和现实社会中的民主选举制度很像,刚开始没有 Leader,所有集群中的参与者都是 Follower, 当 Follower 超过选举超时时间 ( election timeout ) 没收到来自 Leader 的心跳报文,则成为 Candidate,增加任期 ( Term ) 并向其他节点发起新的选举请求 。接收到请求的节点如果还没投票,则投票给该节点,并重置自己的超时时间 。如果某个 Candidate 节点接收到的选票的个数占大多数(超过 1/2), 它就会成为 Leader 节点,这就是所谓的 Leader election 的过程 。每隔一定时间向 Follower 发送心跳报文来维持自己的 『统治』地位 。
集群中的每个节点都处于以上三种角色中的一种,其状态转换图可以总结如下:
 
分布式系统如何保证一致性

文章插图
 
 
下面通过几个例子来帮助我们理解这个选举过程:
1. 可以正常选举出 Leader 的例子 :
 
分布式系统如何保证一致性

文章插图
 
 
在此例子中,一共有三个节点,复现流程如下:
  • 节点 B 首先出现心跳超时,然后变成 Candidate 角色 。
  • 节点 B 首先把自己宝贵的一票投给自己,然后请求其他节点也将赞同票投给自己 。
  • 节点 A 和 C 此时还未出现超时,仍然处于 Follower 角色,接收到请求后,投票给了 节点 B 。
  • 节点 B 接收到的赞同票超过半数,成为 Leader 节点 。
2. 平分选票的情况
由于 Raft 协议并不要求集群中的节点个数为奇数,于是在四个节点的集群中,可能出现如下的情况:
 
分布式系统如何保证一致性

文章插图
 
 
复现流程如下:
  • 节点 B 和 C 几乎同时心跳超时,成为 Candidate 角色 。
  • 节点 B 和 C 均将自己的赞同票投给自己 。
  • 节点 A 投给了节点 B,而 节点 D 则投给了节点 C 。
  • 出现两个 Candidate 的选票相同的情况 。
当出现这种情况的时候,没有任何一个节点接收到的赞同票超过半数,于是此选举过程不会有 Leader 角色出现 。大家各自等待超时,然后开启下一轮的选举流程 。
疑问:那有没有可能在下一轮的选举过程依然出现平分选票的情况?答案是有的,可是 Raft 有一个机制可以避免此种情况持续发生: 每个节点的超时时间都是随机的 ,这样就可以尽量避免多次出现以上的情况 。


推荐阅读