JUST技术:分布式一致性协议概念及Raft协议简介
分布式系统通常由异步网络连接的多个节点构成,每个节点的计算和存储相互独立。分布式一致性指多个节点对某一变量的取值达成一致,一旦达成一致,则变量的本次取值被确定。本文将简单介绍一致性的一些基本概念,以及分布式一致性协议Raft。
一、基本概念
1.1 副本与数据一致性
在分布式系统中,为了保证数据的高可用性,通常会维持数据的多个副本(Replica),这些副本往往会放置在不同的物理机器上。然而,在数据有多份副本的情况下,如果网络、服务器或者软件出现故障,则会导致部分副本写入成功,部分副本写入失败的情况,这就事实上造成了各个副本之间的数据不一致,即数据内容冲突,这便是数据一致性所要解决的问题。
1.2 同步与异步
一句话解释就是,同步是发出调用后,没有结果之前不返回;相反,异步则是发出调用后,立即返回,无返回结果。
举个例子,你打电话问书店老板有没有《分布式系统》这本书,如果是同步通信机制,老板会说,“你稍等,我查一下”。然后就查啊查,有可能查了5秒,也有可能查了3天,最后告诉你结果。此时电话一直未挂断,你一直在等待老板回复。
相反,如果是异步通信机制,你打电话问书店老板有没有《分布式系统》这本书,老板会告诉你,“我知道了”,然后直接挂断了电话。之后等查好了,书店老板会主动打电话给你通知结果。这里老板通过“回电”的方式进行回调,你无需一直等待。
1.3 一致性分类
一致性分类有很多,我们这里主要介绍两种,强一致性(StrongConsistency)和最终一致性(Eventually Consistency)。强一致性指的是,数据一旦写入成功,在任意副本的任意时刻都可读到最新值。最终一致性指的是,写入一个数据成功后,在其它副本有可能读不到最新值,但是在某个时间段窗口后保证最终能读到。
结合同步与异步的概念,强一致性一般要求同步更新所有副本,即所有副本必须都达到最新值,然后才能返回成功,诚然,同步意味着简单,但同时也意味着响应时间更长,吞吐量更低。相反,最终一致性使用的是异步更新方式,通常就会带来更好的吞吐量,但同时也意味着更复杂的架构、开发和调试。
一致性模型本质上是进程与数据存储的约定,通过一致性模型我们可以理解和推理在分布式系统中数据复制所需考虑的问题和基本假设。
1.4 CAP理论
CAP理论又称布鲁尔定理,它指出对于一个分布式系统来说,不可能同时满足以下三点:
1)一致性(Consistency):这里的一致性指的是强一致性;
2)可用性(Availability):系统在正常响应时间内提供服务;
3)分区容错性(Partition Tolerance):发生网络分区时仍可对外服务。其中,一个分布式系统中,节点组成的网络本该相互连通的,然而可能因某些故障如通信断开,导致有些节点之间不连通了,从而将整个网络分成了几块区域,数据则分散在了这些不连通的区域中,这就叫网络分区。
网络分区不可避免,那么想象两个副本分别位于分区两侧,则:1)若允许其中一个副本更新,则会导致数据不一致,即CAP中的C特性丧失;2)若为保证一致性,将分区某一侧副本设置为不可用,则CAP中的A特性丧失;3)除非两侧副本可以相互通信,才可既保证C又保证A,此时CAP中P丧失。然而,P是一定要保证的,因为若没有P,理论上集群不容许任何一个节点发生分区,但是网络分区又是无法避免的,这显然是自相矛盾的。
综上,一个分布式系统,CAP中的三者是无法同时满足的,且P是一定要保证的,那么就只剩下AP与CP模型两个选择。
1.5 ACID原则
ACID原则是关系型数据库的事务机制需要遵守的原则,即:
1)原子性(Atomicity):每次操作都是原子的,即要么成功,要么不执行;
2)一致性(Consistency):这里的一致性是强一致性,即数据库的状态是一直的,无中间状态;
3)隔离性(Isolation):各个事务彼此之间不影响;
4)持久性(Durability):一旦一个事务被提交,应该持久保存,不会因为与其它操作冲突而取消这个事务。
综上,ACID原则是传统数据库的设计理念,追求强一致性。
1.6 BASE理论
关系型数据库是要求强一致性的,这一点在NoSQL数据库中是重点弱化的机制,因为当数据库保持强一致性时,很难保证系统具有横向和可用性的优势,因此分布式数据存储管理只提供了最终一致性的保障,即BASE理论:
1)基本可用(Basically Available):当系统出现故障时,允许损失部分可用性,仅保证核心功能或当前最重要的功能可用。
2)软状态(Soft-state):系统允许存在中间状态,但不会影响系统整体的可用性,即允许不同副本之间存在暂时不一致的情况;
3)最终一致性(Eventually Consistency):无需实时保证数据副本的一致性,仅需最终能够一致即可。例如银行系统中的非实时转账操作,允许24小时内用户账户的状态在转账前后是不一致的,但是24小时后账户数据必须正确。最终一致性是BASE理论的核心,通过弱化一致性,提高系统的可伸缩性、可靠性和可用性。
综上,ACID原则是传统数据库常用的设计理念,追求强一致性;BASE理论支持大型分布式系统,牺牲对强一致性的约束,以换取一定的可用性。二者是对CAP三特性的不同取舍。
二、Raft协议
针对一致性问题产生了许多优秀的一致性协议,常见的有两阶段提交(Two-Phase Commit,2PC),三阶段提交(Three-Phase Commit,3PC),Quorum协议,Gossip协议、Paxos协议和Raft协议等。其中,Raft作为当下最流行的分布式协议之一,以其简单易懂闻名,接下来将简单介绍Raft协议的主要原理。
2.1 复制状态机(Replicated State Machine)
在介绍Raft算法之前,我们先了解下什么是复制状态机。一个分布式系统的复制状态机由多个复制单元组成,每个复制单元均是一个状态机。状态机的状态保存在一组状态变量中,变量只能通过外部指令改变。同时,每个状态机都存储一个包含一系列指令的日志,且严格按照顺序逐条执行日志中的指令。如果所有状态机都能按照日志执行指令,那么所有状态机最终将达到相同的状态。因此,在复制状态机模型下,只要保证了日志的一致性,就能保证分布式系统状态的一致性。
2.2 三个子问题
在Raft协议中,有一个强Leader,由它全权负责接受客户端的请求,并将命令作为日志复制给其它服务器,在确认安全后,将日志提交执行。当Leader故障时,则会选举产生一个新的Leader。在Leader的帮助下,Raft协议将一致性问题简化,分解为了以下三个子问题:
1)领导选举(Leader Election):当现有的Leader故障时,必须重新选出一个新的Leader。
2)日志复制(Log Replication):Leader接收来自客户端的命令,记录为日志,并复制给集群中的其它服务器,且强制其它服务器的日志与Leader保持一致。
3)安全措施(Safety):通过一些措施保证系统安全,例如确保所有服务器均按照相同顺序执行相同命令的措施。
2.3 三种角色
一个Raft集群拥有多个服务器,其典型值是5,这样可容忍2台服务器同时发生故障。服务器可能处于以下3种角色:领导(Leader)、候选人(Candidate)和追随者(Follower)。正常运行情况下有且仅有一个Leader,其它全为Follower,Follower只会响应Leader和Candidate的请求,客户端的请求全部由Leader处理,即使有客户端向Follower发出了请求,也会重定向至Leader进行处理。Candidate出现在领导选举阶段,选举成功的Candidate将会称为新的Leader。三者的状态转换关系如下图:
从上图可以看出,集群在刚启动时,所有节点都是Follower,之后在Time out信号驱动下,Follower会转变成Candidate去拉选票,获得大多数选票后就会成为Leader,此时如果其它Candidate发现新的Leader已经产生,就会自动转变为Follower;而如果另一个Time out信号发出时,还没有选举出Leader,将会重新开始一次新的选举。可见,Time out信号是角色转换的关键因素,类似于操作系统中得中断信号。
Raft将时间分为了任意长度的时间片,称为Term,Term使用连续且递增的编号进行识别,如下图所示:
每一个Term都从新的选举开始,Candidate一旦获胜成为Leader,它就会在剩余的Term内保持Leader状态。在某些情况下如Term 3,选票可能被多个Candidate瓜分,无法形成大多数,因此Term 3可能直至结束都没有Leader,但下一个Term很快就会到来重新发起选举。
每一个服务器都存储了当前Term编号,在服务器之间进行交流的时候就会带上该编号,如果一个服务器的编号小于另一个的,那么它就会将自己的编号更新为较大的那一个;如果Leader或Candidate发现自己的编号不是最新的了,就会自动转变为Follower;如果接收到的请求的Term编号小于自己的当前Term将会拒绝执行。
2.4 两种RPC
节点之间的交流都是通过RPC(Remote Procedure Call,远程过程调用,简单的理解是一个节点请求另一个节点提供的服务),在Raft协议种,仅需实现两种RPC就可构建一个基本的集群。
1)RequestVote RPC:由领导选举过程中的Candidate发起,用于拉取选票。
2)AppendEntries RPC:由Leader发起,用于复制日志或发送心跳信号。
2.5 领导选举过程
Raft通过心跳机制发起Leader选举。节点都是从Follower状态开始,如果收到了来自Leader或Candidate的RPC,那它就保持Follower状态,避免争抢成为Candidate。Leader会发送空的AppendEntries RPC作为心跳信号来确立自己的地位,如果Follower一段时间(Election Timeout)没有收到心跳,它就会认为Leader已经挂了,发起新的一轮选举。
选举发起后,Follower会增加自己的当前Term编号并转变为Candidate。它会首先投自己一票,然后向其他所有节点并行发起RequestVoteRPC,之后Candidate状态将可能发生如下三种变化:
1)赢得选举,成为leader:如果它在一个Term内收到了大多数的选票,那么将会在接下的剩余Term时间内成为Leader。每一个节点在一个Term内只能投一张选票,并且按照先到先得的原则投出。
2)其他节点成为Leader:在等待投票时,可能会收到其他节点发出AppendEntriesRPC心跳信号,说明新的Leader已经产生了。这时通过比较自己的Term编号和RPC过来的Term编号,如果比对方大,说明Leader的Term过期了,就会拒绝该RPC,并继续保持Candidate; 否则承认对方的地位,转为Follower。
3)选票被瓜分,选举失败:如果没有Candidate获取大多数选票, 即没有Leader产生,则Candidate等待超时后发起另一轮选举。为了防止下一次选票还被瓜分,Raft采用随机Election Timeout的机制,通过将Timeout随机设为一段区间上的某个值,因此很大概率会有某个Candidate率先超时然后赢得大部分选票。
2.6 日志复制过程
客户端提交每一条命令都会被按顺序记录到Leader的日志中,每一条命令都包含Term编号和顺序索引,然后向其他节点并行发送AppendEntries RPC用以复制命令,当大多数节点成功复制后,Leader就会提交命令,即执行该命令并且将执行结果返回客户端,Raft保证已经提交的命令最终也会被其他节点成功执行。Leader会保存有当前已经提交的最高日志编号。顺序性确保了相同日志索引处的命令是相同的,而且之前的命令也是相同的。当发送AppendEntries RPC时,会包含Leader上一条刚处理过的命令,接收节点如果发现上一条命令不匹配,就会拒绝执行。
以上便是Raft协议的主要内容,这里只是简单介绍,详情请见论文[3],还有一些很棒的Raft可视化帮助理解[1][2]。
目前,我们JUST团队正在基于Raft协议,实现分布式时空数据查询引擎,敬请关注!
参考文献:
[1] https://raft.github.io/
[2] http://thesecretlivesofdata.com/raft/
[3] Ongaro D,Ousterhout J. In search of an understandable consensus algorithm.
0 条评论