共识算法演绎 :从 paxos 小岛说起
Posted Unitimes
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了共识算法演绎 :从 paxos 小岛说起相关的知识,希望对你有一定的参考价值。
unitimes.media
全球视角,独到见解
“世上只有一种一致性算法,那就是Paxos。——Mike Burrows(Google Chubby)”
1998年5月,Leslie Lamport在《ACM Transactions on Computer Systems》发表了题为《兼职议会(The Part-Time Parliament )》的论文1,这一文章中的论述标志着著名的paxos算法的诞生,由此掀开了分布式共识算法应用的帷幕。
事实上,Leslie Lamport关于paxos的论文早于1990完成。早在1982年Leslie Lamport就在《ACM Transactions on Programming Languages and Systems》发表了如今家喻户晓的“拜占庭将军问题2”。所谓“拜占庭将军问题”,是指信息在传达的过程中可能遭遇恶意删改,但仍然能够保证系统内每一个节点最终就正确的信息达成一致。拜占庭战争的故事为枯燥的文章增色不少,尝到了甜头的老爷子乘势又虚拟了一个关于希腊paxos小岛的故事来讲述paxos算法。但这一次,paxos小岛的故事并没有引起人们的重视。直到1998年,这一篇讲述分布式一致性算法的文章才得以发表。三年后,Leslie Lamport重新正儿八经地阐述了paxos算法(即2001版《Paxos Made Simple》3)。时隔17年,我们依稀能从论文简要的摘要中读出深深的怨念。
图1 偌大的白纸只写着一句话:paxos,简单的很
为了致敬这位伟大的分布式计算大师,我们的共识机制演绎,从paxos小岛说起。
“
Paxos
”
paxos岛坐落于爱琴海,从前曾是极度繁荣的商业中心,后毁于外敌入侵。后世对paxos岛的了解大多局限于考古学家们有限的研究。从前这里经济发达,政治精明,议会制早已取代了神权统治。但对于paxos岛上的居民来说,财富远高于市民责任,没有人愿意终其一生都献身给议会。因此,议会的议员全都是兼职的。
现代议会一般会有专职秘书进行记录,但paxos议会则是议员人手一本记事簿,簿内记载着每一项通过的议案,其记载格式如下:
[法令编号]:法令内容
这种议案的记录由永不褪色的墨水书写,也就是说,一旦议案确立,其将不可更改。
图2 尼古拉斯 · 赵四的遗作
“第155号法令:橄榄税为3德拉克马/吨”
此外,paxos小岛民风淳朴,街坊邻里相互信任,不管是什么议案大家乐意通过。尽管议员们十分不安定,但他们的内心还是善良的,他们保证:在议会工作的每一刻都能够兢兢业业,即时地反馈意见。如果在每一轮投票阶段,超过半数议员留在议会并进行表决,那么总有议案会被通过。
paxos议会的制度是在后教会制度的基础上发展而来的。在早期教会中,每一项议案都要遵从传统程序由所有牧师与会表决。随着paxos商业贸易日益发达,牧师们也开始为生意奔波,教会制度由此崩溃。为了避免重蹈覆辙,paxos的宗教领袖们聘请了一批数学家重新设计出后教会制度,即paxos算法的雏形。
在后教会制度中,为了保证议案有序通过,数学家们首先制定了3条规则:
1. 每一轮投票中,每一项议案都有一个唯一的编号(即上述法令编号)。其中,我们约定每一轮投票均包含若干议案,直到最终某一议案获得通过。
2. 每一轮投票中,对任意两次议案进行表决的法定集合中,至少有一位重合的牧师。
3. 在对任何一项议案进行表决时,如果某一议案(记为议案A)法定集合内的牧师参与了以前另一议案(记为议案B,C,D)的表决,议案A的法令内容与议案B,C,D中编号最大的议案的法令内容相同。
关于某一议案(比如议案A)的法定集合,此处可理解为获知议案A的超过半数的与会牧师。在原文的阐述中,牧师们获知某一议案不是实时的,这些议案是由兼职传达的。也就是说,议案在传达的过程中会出现种种意外(比如传信的兼职中途回家吃饭)导致议案无法顺利到达特定牧师手中。法定集合的限制保证了“少数服从多数的原则”,由此完成共识。
假设教会内共有5名牧师,那么每一项议案的法定集合是3个人(或以上)。我们假设表决通过议案D的牧师为a,b,c,而在下一项议案E(E>D)中,假设其法定集合为a,c,d,其中,a,c此前已经表决了议案D,d可能没有表决或者假设其表决了议案B(B<D),那么依据第3项规则,议案E的法令内容应当与议案D的法令内容相同,因为D是E的法定集合内的牧师表决过的离E最近的一项议案。以此类推,后续比D大的议案(比如X,Y,Z)内的法令内容都与D相同。
然而,这种方式尽管能够保证牧师们就议案达成一致,却也无法正常开展下一轮投票。基于此,数学家们推导出了更完善的协议。
首先,每一项议案都由一位牧师提出。这些牧师不仅要决定议案的编号,法令内容,还要选定法定集合。但这里面就有一个问题,议案编号、法令内容以及法定集合该如何选定呢?此外,被选定的法定集合内的牧师该遵循怎样的表决标准呢?
为了满足第1项规则,每一位牧师必须记住他先前提出的议案的编号,由此保证其个人提议的议案编号不会重复。此外,为了避免不同的牧师采用相同的议案编号,我们可以把这些编号分割开来,比如数字1~10,我们可以让牧师A用奇数编号,牧师B用偶数编号。另一种更显然的方式,是每个数字后面加上个人的姓氏,比如[13 张三],[13 李四]。
为了满足第2项规则,法定集合可以简单地挑选为超过半数的人数。在原文中,作者认为也可以依据体重总和超过半数体重的人员组合的方式来选定法定集合,即我们熟知的工作量证明或权益证明。
在阐述如何实现第3项规则之前,我们首先引入MaxVote(b, q, B)dec和MaxVote(b, Q, B)dec变量。其中,MaxVote(b, q, B)dec代表其他牧师(记为牧师p)所表决过的编号最大的议案,如果牧师p此前没有对任何议案进行表决,那么MaxVote(b, q, B)dec代表的就是“空(nullq )”。当提议新议案的牧师(记为牧师q)收到来自其他牧师的MaxVote(b, q, B)dec以后,他会从中挑选出MaxVote(b, q, B)dec中编号最大的议案作为MaxVote(b, Q, B)dec。
如果牧师所得到的MaxVote(b, Q, B)dec非空,那么牧师所提议的议案的法令内容必须是MaxVote(b, Q, B)dec的法令内容;相反,如果MaxVote(b, Q, B)dec为空,那么牧师可以提议任意编号与法令。
因此,为了满足第3项规则,牧师必须获知其所通知的法定集合内的所有牧师的MaxVote(b, q, B)dec。
综上,牧师p在提议议案的第一阶段,首先要:
(1) 挑选一个编号X,并把这个新的议案a告知其他牧师。
(2) 牧师q在收到关于编号为X的议案后,会把自己所表决过的编号最大的议案(或者“空”)返回给牧师p。
因为牧师p要从众多MaxVote(b, q, B)dec里面决定MaxVote(b, Q, B)dec,为了满足第3项原则,其他牧师在对新议案的请求提出反馈(即告知MaxVote(b, q, B)dec)以后,MaxVote(b, q, B)dec就不能发生改变。为此,在对编号为X的议案进行回应以后,牧师们要发誓不再接收任意编号小于X的议案。
(3) 在收到其他牧师(这些牧师的数量或其他指标需要满足法定集合的条件)的反馈以后,牧师p将会把完善后的议案a’(编号X,并添加上与MaxVote(b, Q, B)dec相同的法令内容)重新告知其他牧师,并把这一议案记录在记事簿的背面。
(4) 牧师q在收到议案a’以后,会决定是否对议案a’进行表决。如果他认为对议案a’会违背他对其他牧师的承诺(比如他已经承诺了不再接受任何编号小于X+1的议案),那么他将不会表决。否则,牧师q在对议案a’ 进行表决以后,他会把这一表决记在记事簿的背面。
*在原文中,记在记事簿背面的内容只是为了记忆,必要时可进行删改,但记在记事簿正面的内容是不可更改的。
至此,提案表决的流程已经基本结束。接下来的问题是,牧师该如何就议案a’的最终通过达成一致。下述补充两个步骤:
(5) 如果牧师p收到了来自其所选定的法定集合内的每一位牧师对议案a’的表决,那么他就会把议案a’记录在记事簿的正面,并把议案a’通过的消息告知议会内的所有牧师。
(6) 在收到议案a’通过的消息以后,牧师们也把议案a’记录在记事簿的正面。
为了简化牧师们的工作量,牧师们每一次只需要在记事簿背面记录三个变量:lastTried[p]、prevVote[p] 、nextBal [p] 即可。其中,lastTried[p]表示该牧师最近一次提出的议案的编号,如果没有,则置为−∞ ;prevVote[p] 表示该牧师曾经参与过的表决行为,但只记录最近一次(即编号最大的)议案的表决,如果没有,则置为−∞ ;nextBal [p]表示该牧师最近一次对提出议案的牧师所返回的MaxVote(b, q, B)dec,如果没有,则置为−∞ 。
上述步骤尽管已经有效解决了牧师们如何就议案表决达成一致的问题。但是并没有给出提出议案的具体方法。试想,如果牧师们谁也不愿意提出议案,那么这个教会也就没有存在的必要了。反之,如果牧师们都抢着提出议案,那么议案也很可能无法达成一致。考虑一种情况:如果牧师q已经接受了牧师p提出的关于编号为X的议案a的请求,并承诺不再接受任何编号小于X的议案。这时,牧师M提出了一个X+1的议案b,于是牧师q又接受了议案b,并承诺不再接受任意编号小于X+1的议案(这意味着议案a从此被牧师q拒绝了),依此类推。
为了保证议案的进程能够正常开展,我们规定只有在议案成功通过以后才能接着提出新的议案。为了设计出完整的步骤,我们需要计算每一位牧师处理事务的时间以及传信者传话所需要的时间。经过测算,在牧师和传信者中途不跑路的情况下,牧师大概能在7分钟内完成事务,传信者能在4分钟内完成任务。由此可推算,在步骤(1)(2)中,牧师p从把议案a告知牧师q,再到牧师q把信息反馈给牧师p,需要花费一个来回,即7+4+7+4=22分钟。换而言之,依照步骤(1)~(6),如果牧师p在步骤(1)提出新的议案,那么他应该在22分钟以内执行步骤(3),在44分钟以内执行步骤(5)。
为了保证议案a能顺利通过,必要时,牧师p还需要获知是否存在比议案a编号X更大的编号。为此,我们要求,如果牧师q在收到议案a时,发现其此前已经接受了编号比X更大的议案b时,应该及时把议案b告知牧师p。此后,牧师p将提出编号更大的议案a’’。
假设教会的门被锁死了,牧师和传信者们只能乖乖地完成工作。那么99分钟以内必有议案被通过。为什么是99分钟呢?假设牧师p向其所选定的法定集合内的牧师告知议案a,这时,牧师q告知牧师p,他获知了比X更大的议案b,这一过程来回共花费22分钟。为了把保证议案能够顺利通过,牧师p必须保证议案的编号足够大,这时,他会向法定集合内的牧师获取所有比X大的编号信息。这里的编号信息不仅仅是指牧师q反馈给牧师p的“已经有编号比X更大的议案b”的信息,还包括前述的lastTried[p] ,即该牧师自己提议过的比X更大的编号,这一过程来回也需要花费22分钟。此后,牧师p就可以用更大的编号Y来提出新的议案a’’。如前所述,步骤(1)(2)分别需要22分钟,步骤(3)(4)分别需要22分钟,步骤(5)(6)需要花费11分钟(这里的11分钟指牧师p把议案通过的信息告知给牧师q,因为此后已经达成共识,所有牧师q记录的时间不再计算,也不再需要传信者再跑一趟找牧师p)。
那么负责提出议案的牧师p(下称主牧师)该如何寻找呢?前面已经说过,如果太多牧师在一定时间内同时提出议案,务必会阻碍正常进展。不妨规定,主牧师依据所有与会人员的姓名排序来决定。此外,如果在一定时间内,与会人员没有发生变化,那么T分钟后,每个人都可以认为自己是主牧师。
什么意思呢?也就是说,如果一位牧师至少每隔T-11分钟(牧师7分钟,传信者4分钟)就发送一次与个人姓名相关的信息,且T分钟后没有姓名排序比他更高的人跳出来,那么这位牧师就可以认为自己是主牧师了。
当然,在确定主牧师以后,牧师们可以相互交换lastTried[p] 以保证主牧师第一次能够提出足够大的编号。
以上就是paxos小岛的故事。
为了便于读者理解,上述故事仅保留了必要的推导,并结合2001年版《Paxos Made Simple》对原文故事进行了改述。对应到paxos的实际应用,牧师q代表对议案进行表决的接受节点(acceptor),主牧师就是提出议案的提案节点(proposer)。
事实上,在1998年《The Part-Time Parliament》论文发表以后,分布式计算算法吸引了越来越多人深陷其中。此后20年里,诞生了一大批关于paxos的改进及优化算法,感兴趣的朋友可以点击http://paxos.systems/index.html进行查看。
参考文献
[1] Leslie Lamport. The Part-Time Parliament [J]//ACM Transactions on Computer Systems, 1998,5: 133-169
[2] Leslie Lamport. The Byzantine Generals Problem[J]//ACM Transactions on Programming Languages and Systems, 1982,7:Vol. 4, No. 3.
[3] Leslie Lamport. Paxos Made Simple[J]//ACM SIGACT News,2001,12
国际金融科技新媒体和社区平台
UNITIMES
网址 : unitimes.media
新浪微博:@Unitimes
以上是关于共识算法演绎 :从 paxos 小岛说起的主要内容,如果未能解决你的问题,请参考以下文章