分布式平台基础算法浅析
一、CAP基础理论
1.1 CAP需求
在计算机科学中, CAP 理论又称之为布鲁尔定理(Brewer's theorem),目前已成为分布式系统设计与构建的重要理论基石。其中CAP即Consistency(一致性)、Availability(可用性)及Partition Tolerance(分区容错性)这三个系统核心功能的简称。
1.1.1 一致性
在分布式系统中,数据存在多个副本。一致性是指对某数据操作之后,存在各副本中的该数据始终保持一致。一致性操作是原子性操作,即对数据的更新是对所有副本全部成功才算操作成功,否则为失败。如果操作失败,则回退到一致性操作前的状态。
1.1.2 可用性
可用性是指客户端访问数据时,可以得到响应,但系统可用并不代表存储系统的所有节点提供的数据一致。往往对于不可用的应用设定一个定长响应时间,超过这个定长响应时间的服务认为不可用。可用性只表明服务可用,主要标志是可以完成或不能完成上述操作。
1.1.3 分区容忍性
在分布式系统中,为了提供系统性能,可以将同一数据的副本存放在不同地点。分区容错性是指福布斯系统的容错性。更确切的说,除非网络的全部节点都出现故障,否则所有子集合节点的故障都不能导致整个系统的不准确响应。分区容错性也可以理解为在存在网络分区的情况下仍然可以接受满足一致性或可用性的请求。
1.2 CAP定理
CAP定理指出:在一个福布斯系统构建中,不可能同时满足一致性、可用性和分区容错性三个系统的需求,最多只能同时满足两个。对所设计系统的强调点不同,采用的策略也不一样。
分布式系统在小规模、低压力、小延迟的情况下,CAP定理还不足以对系统总体性能造成影响,但随着活动增加与吞吐量上升将凸显其重要性。对大型网站来说,可用性与分区容错性的优先级高于数据一致性,一般放弃一致性,尽量朝着可用性和分区容错性的方向设计,然后通过其他方法来保证一致性的商务需求。
1.3 CAP理论证明
假设两个节点集 {G1,G2},由于网络分片导致 G1 和 G2 之间所有的通讯都断开了,如果在 G1 中写,在 G2 中读刚写入的数据, G2 中返回的值不可能是 G1 中的写入值。由于可用性的要求,G2 一定要返回这次读请求,由于分区容错性的存在,导致不一致性。但由于一致性的要求, G2 一定要返回这次读请求的一致性结果,由于分区容错性的存在,导致了长期等待,出现了不可用性。
1.4 衍生模型
BASE 模 型 在 20 世 纪 90 年 代 末 提 出, 基于同性质之间的取舍,考虑了高可用性的设计。BASE(Basically Available, Soft-state, Eventuallyconsistent)是基本可用、软状态、最终一致性的英文缩写。 BASE 思想主要强调基本的可用性,显然 BASE 支持最终一致的概念。
BASE理论是CAP理论结合解决实际问题的产物,针对大规模跨区域分布的系统,均采用了这种思路。
这里提到的一致性问题,我们将数据的一致性根据实现时间的不同分为三个种类:强一致性、弱一致性及最终一致性三种程度。
1)强一致性。强一致性也成为即时一致性,加入A操作先写入了一个值到储存系统,存储系统保证后续A、B、C的读取操作都将返回最新值。
2)弱一致性。假如A先写入一个值到存储系统,系统不能保证后续A、B、C的读取操作能读到最新值,即A写完的数据并不能立刻读到。
3)最终一致性。最终一致性是弱一致性的一种特例,是指系统需要在某一刻后达到一致性要求。假如A首先写了一个值到存储系统,存储系统保证如果在A、B、C后续读取之前没有其他操作更新同样的值,最终所有的读取操作都将读取到A写入的最新值。即最终数据时一致的就可以了,而不是实时一致。典型代表系统可以说是DNS系统,当更新一个域名IP以后,根据配置策略以及缓存控制策略的不同,所有的客户最终都会看到最新的值。
其中提及的基本可用含义是指系统能够基本运行、始终提供服务。BASE模型与ACID模型相反,通过牺牲高一致性,获得可用性和分区容错性。
软状态是指系统不要求一直保存强一致状态,可以有一段时间不同步。软状态也可以理解为无连接的,而硬状态是有连接的。
1.5 总结
CAP理论中抽象出系统运行时最重要的三个原则,同时从理论层面证明了一个分布式系统在某种程度上最多只能同时满足两个原则,因此对于不同的“三中取二”原则我们能得到不同的符合自己应用场景的产品。
二、Paxos算法及其衍生算法
2.1 Paxos算法剖析
2.1.1 背景简介
Paxos是Leslie Lamport于1990年提出的一种基于消息传递的一致性算法,主要用于解决一个分布式系统如何就某个值达成一致。从工程实践意义上来说,就是可以通过Paxos实现多副本一致性、分布式锁、名字管理、序列号分配等。比如,在一个分布式数据库系统中,如果各节点的初始状态一致,每个节点执行相同的操作序列,那么他们最后能得到一个一致的状态。为保证每个节点执行相同的命令序列,需要在每一条指令上执行一个“一致性算法”以保证每个节点看到的指令一致。以下在分析Basic Paxos算法时主要描述二阶段提交过程。
2.1.2 概念与术语
Proposer:提议发起者,处理客户端请求,将客户端的请求发送到集群中,以便决定这个值是否可以被批准。
Acceptor:提议批准者,负责处理接收到的提议,他们的回复就是一次投票,会储存一些状态来决定是否接收一个值。
replica:节点或者副本,分布式系统中的一个server,一般是一台单独的物理机或者虚拟机,同时承担paxos中的提议者和接收者角色。
ProposalId:每个提议都有一个编号,编号高的提议优先级高。
Paxos Instance:Paxos中用来在多个节点之间对同一个值达成一致的过程,比如同一个日志序列号。
logIndex,不同的logIndex属于不同的Paxos Instance。
acceptedProposal:在一个Paxos Instance内,已经接收过的提议
acceptedValue:在一个Paxos Instance内,已经接收过的提议对应的值。
minProposal:在一个Paxos Instance内,当前接收的最小提议值,会不断更新。
2.1.3 Basic-Paxos算法
基于Paxos协议构建的系统,只需要系统中超过半数的节点在线且相互通信正常即可正常对外提供服务。它的核心实现Paxos Instance主要包括两个阶段:准备阶段(prepare phase)和提议阶段(accept phase)。如下图所示:
1.获取一个ProposalId,为了保证ProposalId递增,可以采用时间戳+serverId方式生成。
2.提议者向所有节点广播prepare(n)请求。
3.接收者比较n和minProposal:如果n>minProposal,表示有更新的提议,minProposal=n;否则将(acceptedProposal,acceptedValue)返回。
4.提议者接收到过半数请求后,如果发现有acceptedValue返回,表示有更新的提议,保存acceptedValue到本地,然后跳转1,生成一个更高的提议。
5.到这里表示在当前paxos
instance内,没有优先级更高的提议,可以进入第二阶段:广播accept(n,value)到所有节点。
6.接收者比较n和minProposal,如果n>=minProposal,则acceptedProposal = minProposal = n,acceptedValue=value,本地持久化后,返回;否则,返回minProposal。
7.提议者接收到过半数请求后,如果发现有返回值>n,表示有更新的提议,跳转1;否则value达成一致。
从上述流程可知,并发情况下,可能会出现第4步或者第7步频繁重试的情况,导致性能低下,更严重者可能导致永远都无法达成一致的情况,就是所谓的“活锁”,如下图所示:
1.S1作为提议者,发起prepare(3.1),并在S1,S2和S3达成多数派;
2.随后S5作为提议者 ,发起了prepare(3.5),并在S3,S4和S5达成多数派;
3.S1发起accept(3.1,value1),由于S3上提议 3.5>3.1,导致accept请求无法达成多数派,S1尝试重新生成提议
4.S1发起prepare(4.1),并在S1,S2和S3达成多数派
5.S5发起accpet(3.5,value5),由于S3上提议4.1>3.5,导致accept请求无法达成多数派,S5尝试重新生成提议
6.S5发起prepare(5.5),并在S3,S4和S5达成多数派,导致后续的S1发起的accept(4.1,value1)失败。
从Basic-Paxos的描述可知,需要通过两阶段来最终确定一个值,由于轮回多,导致性能低下,至少两次网络RTT。那么prepare阶段能否省去?如下图所示:
1.S1首先发起accept(1,red),并在S1,S2和S3达成多数派,red在S1,S2,S3上持久化
2.随后S5发起accept(5,blue),对于S3而言,由于接收到更新的提议,会将acceptedValue值改为blue
3.那么S3,S4和S5达成多数派,blue在S3,S4和S5持久化
4.最后的结果是,S1和S2的值是red,而S3,S4和S5的值是blue,没有达成一致。
2.1.4 Multi-paxos算法
Paxos是对一个值达成一致,Multi-Paxos是连续多个paxos instance来对多个值达成一致,这里最核心的原因是multi-paxos协议中有一个Leader。Leader是系统中唯一的Proposal,在lease租约周期内所有提案都有相同的ProposalId,可以跳过prepare阶段,议案只有accept过程,一个ProposalId可以对应多个Value,所以称为Multi-Paxos。
2.1.4.1 选举
首先我们需要有一个leader,其实选主的实质也是一次Paxos算法的过程,只不过这次Paxos确定的“谁是leader”这个值。由于任何一个节点都可以发起提议,在并发情况下,可能会出现多主的情况,比如A,B先后当选为leader。为了避免频繁选主,当选leader的节点要马上树立自己的leader权威(让其它节点知道它是leader),写一条特殊日志(start-working日志)确认其身份。根据多数派原则,只有一个leader的startworking日志可以达成多数派。leader确认身份后,可以通过了lease机制(租约)维持自己的leader身份,使得其它proposal不再发起提案,这样就进入了leader任期,由于没有并发冲突,因此可以跳过prepare阶段,直接进入accept阶段。通过分析可知,选出leader后,leader任期内的所有日志都只需要一个网络RTT(Round Trip Time)即可达成一致。
2.1.4.2 新主恢复流程
由于Paxos中并没有限制,任何节点都可以参与选主并最终成为leader,这就无法保证新选出的leader包含了所有日志,可能存在空洞,因此在真正提供服务前,还存在一个获取所有已提交日志的恢复过程。新主向所有成员查询最大logId的请求,收到多数派响应后,选择最大的logId作为日志恢复结束点,这里多数派的意义在于恢复结束点包含了所有达成一致的日志,当然也可能包含了没有达成多数派的日志。拿到logId后,从头开始对每个logId逐条进行paxos协议,因为在新主获得所有日志之前,系统是无法提供服务的。为了优化,引入了confirm机制,就是将已经达成一致的logId告诉其它acceptor,acceptor写一条confirm日志到日志文件中。那么新主在重启后,扫描本地日志,对于已经拥有confirm日志的log,就不会重新发起paxos了。同样的,在响应客户端请求时,对于没有confirm日志的log,需要重新发起一轮paxos。由于没有严格要求confirm日志的位置,可以批量发送。为了确保重启时,不需要对太多已提价的log进行paxos,需要将confirm日志与最新提交的logId保持一定的距离。
2.1.4.3 性能优化
Basic-Paxos一次日志确认,需要至少2次磁盘写操作(prepare,promise)和2次网络RTT(prepare,promise)。Multi-Paxos利用一阶段提交(省去Prepare阶段),将一次日志确认缩短为一个RTT和一次磁盘写;通过confirm机制,可以缩短新主的恢复时间。为了提高性能,我们还可以实现一批日志作为一个组提交,要么成功一批,要么都不成功,这点类似于group-commit,通过RT换取吞吐量。
2.1.4.4 安全性(异常处理)
1.Leader异常
Leader在任期内,需要定期给各个节点发送心跳,已告知它还活着(正常工作),如果一个节点在超时时间内仍然没有收到心跳,它会尝试发起选主流程。Leader异常了,则所有的节点先后都会出现超时,进入选主流程,选出新的主,然后新主进入恢复流程,最后再对外提供服务。我们通常所说的异常包括以下三类:
(1)进程crash(OS crash)
Leader进程crash和Os crash类似,只要重启时间大于心跳超时时间都会导致节点认为leader挂了,触发重新选主流程。
(2).节点网络异常(节点所在网络分区)
Leader网络异常同样会导致其它节点收不到心跳,但有可能leader是活着的,只不过发生了网络抖动,因此心跳超时不能设置的太短,否则容易因为网络抖动造成频繁选主。另外一种情况是,节点所在的IDC发生了分区,则同一个IDC的节点相互还可以通信,如果IDC中节点能构成多数派,则正常对外服务,如果不能,比如总共4个节点,两个IDC,发生分区后会发现任何一个IDC都无法达成多数派,导致无法选出主的问题。因此一般Paxos节点数都是奇数个,而且在部署节点时,IDC节点的分布也要考虑。
(3).磁盘故障
前面两种异常,磁盘都是OK的,即已接收到的日志以及对应confirm日志都在。如果磁盘故障了,节点再加入就类似于一个新节点,上面没有任何日志和Proposal信息。这种情况会导致一个问题就是,这个节点可能会promise一个比已经promise过的最大proposalID更小的proposal,这就违背了Paxos原则。因此重启后,节点不能参与Paxos Instance,它需要先追上Leader,当观察到一次完整的paxos instance时该节点结束不能promise/ack状态。
2.Follower异常(宕机,磁盘损坏等)
对于Follower异常,则处理要简单的多,因为follower本身不对外提供服务(日志可能不全),对于leader而言,只要能达成多数派,就可以对外提供服务。follower重启后,没有promise能力,直到追上leader为止。
参考文献
[1] 陈明. 分布系统设计的CAP理论[J]. 计算机教育,2013,(15):109-112.
[2] 天士梦. 分布式一致性算法--Paxos https://www.cnblogs.com/cchust/p/5617989.html 2016/06/27
[3] Lamport L. Paxos Made Simple[J]. Acm Sigact News, 2016, 32(4).
[4] Lamport L. The part-time parliament[M]. ACM, 1998.
[5] 郑建军. 微信PaxosStore:深入浅出Paxos算法协议 http://www.infoq.com/cn/articles/wechat-paxosstore-paxos-algorithm-protocol# 2016/12/29