天天看点

一篇文章带你了解Paxos算法Russell Cohen的回答

本文讲的是<b>一篇文章带你了解Paxos算法</b>,【编者的话】本文是Quora上关于Paxos算法的回答,两位答者分别从不同的角度描述Paxos算法。Vineet Gupta的回答细致入微,更偏向理论。Russell Cohen用具体的例子讲解Paxos算法,相辅相成。

有很多关于一致性(consensus)问题的解决方案,而这些解决方案中,我认为Paxos相对来说很好理解。

『达成一致性』最简单的例子就是结婚誓词:

“你愿意.......”(男:)“我愿意!”(女:)“我愿意!”

“现在我宣布你们...”

现在假设婚姻并非只有两个人,就像罗伯特·乔丹的《时间之轮》中的

“你愿意...?”(男:)“我愿意!”(女1:)“我愿意!”(女2:)“我愿意!”...

如果任何一个将会成为配偶的艾尔人不回应“我愿意”,那这场婚礼将无法继续。

表决阶段。在表决阶段,协调者将通知事务参与者准备提交或取消事务,然后进入表决过程。在表决过程中,参与者将告知协调者自己的决策:同意(事务参与者本地作业执行成功)或取消(本地作业执行故障)。

提交阶段。在该阶段,协调者将基于第一个阶段的投票结果进行决策:提交或取消。当且仅当所有的参与者同意提交事务协调者才通知所有的参与者提交事务,否则协调者将通知所有的参与者取消事务。参与者在接收到协调者发来的消息后将执行响应的操作。

要注意的是票只能投给(协调器)建议的值,每个节点只能说是或否。节点不能再提出一个可供选择的值。如果一个节点想提出自己的 值,它需要开始它自己的2PC。算法很简单,由节点来决定某一个节点提出的值。它也不是非常低效的,对于N个节点,将交换3N条消息。

但是如果一个节点崩溃了会发生什么?例如,假设在阶段一协调器将提议的值发送给部分节点(并非全部)后,然后协调器就挂掉了。(这是会发生什么?) 

现在一些节点已经开始了2PC循环,而另一些节点没有意识到2PC循环已经发生。那些已经开始2PC循环的节点被阻塞在等待下一个阶段上。

在我们的场景中,已经投票的资源管理单元也可能不得不锁定一些资源,这样资源管理不会因为等待协调器恢复并启动阶段二而耗尽时间。

如果阶段二中的协调器未能把所有的提交信息发送给全部节点而只是部分节点后就崩溃了,相似的问题依然存在。我们可以让另一个节点接管协调器职责观察时间超时来解决部分存在的问题。这个节点会和其它节点取得联系并获得它们的投票情况(要求它们必须投票)以及推动事务完成,但这一过程中进一步参与者可能发生故障,同时事务可能永远不会恢复。我们的底线-在节点故障的情况下2PC无法可靠地操作。

2PC的关键问题是,万一协调器挂掉了,没有节点具备足够的信息来完成整个协议。这一点可以通过添加额外的步骤来解决:

1. 阶段1和(2PC)相同-协调器给所有的节点发送一个值。

2. 阶段2-新的步骤-一旦从上一步中所有节点都接收到“是”,协调器发送出“准备提交”消息。我们期望的是节点能够执行可以撤消的工作,而且没有什么是不能撤消的。每一个节点向协调器确认它已经接收到“准备提交”消息了。

3. 阶段3-类似2PC中阶段二-如果协调器从所有节点接收到关于“准备提交”的确认信息,它就继续工作,将投票的结果发送给所有的节点要求他们提交。但是,如果所有节点都不确认,协调器会终止事务。

现在如果协调器在任何时刻崩溃,任何参与者都可以接管协调器的角色并查询来自其它节点的状态。

如果任何资源管理向恢复节点报告说它并没有接收到“准备提交”的信息,恢复节点便知道事务没有提交给任何资源管理。现在要么事务被悲观地终止,要么协议实例重新运行。

如果有一个管理单元提交事务后崩溃了,我们知道的是,其它每一个资源管理单元可能会收到“准备提交”的确认信息,否则协调器不会进入提交阶段。所以协调器是可以进入到最后一个阶段的。

因此3PC能够很好地工作尽管有节点故障。这是以在N个节点添加一个新的步骤为代价的,它导致了较高的延时。

3PC在网络分区情况下存在不足。假设所有收到“准备提交”信息的资源管理都在分区的一侧,其余的都在另一边。现在这会导致每个分区都选择一个恢复节点,这两个恢复节点要么提交事务,要么终止事务。一旦网络分区被移除,系统会得到不一致的状态。

先说重要的事-考虑下3PC,我们还需要更好的算法吗?3PC唯一的问题是网络分区,真的吗?首先,我们假设网络分区是唯一的问题(并不是,下面我们会看到)。在网络分区的情况下,正确性真的是值得解决的问题吗?今天,在云计算和互联网规模的服务下,其中的节点有可能在大陆的不同侧或者跨越了大洋,我们确实需要分区容错算法。

第二点是网络分区并不是唯一的问题。当我们解决了单个节点永久故障的情况时,更一般的情况是,节点崩溃了,接着它重新恢复并且从崩溃的地方工作。这种<code>故障-恢复模式</code>也可以描述一个异步网络模型,这种网络模型中一个节点用来响应消息的时间是没有上限的,因此你不能假设一个节点死了-也许它仅仅是响应缓慢或者是网络缓慢。在这种模型中你不能判断超时。

3PC是<code>故障-停止</code>容错,而不是<code>故障-恢复</code>容错。不幸的是现实生活中需要的是<code>故障-恢复</code>容错,因此我们需要更一般的解决方案。而这正是Paxos的用武之地。

Paxos中的基本步骤和2PC很像:

选择一个节点作为领导者/提议者。

领导者选择一个值并将它发给所有节点(Paxos中被称为接收者),(这个值)封装在<code>接收-请求</code>消息中,接收者可以回复拒绝或接受。

一旦多数节点都接受,共识就达成了同时协调器向所有节点广播<code>提交</code>信息。

Paxos与2PC主要不同点在于Paxos不要求所有节点都要同意(才会提交事务),只要大部分节点同意就行。这是一个有趣的想法因为在两个独立的多数节点中至少存在一个正常工作的节点。这确保在给定的循环中,如果大部分节点赞同给定的值,任何随后努力提出一个新值的节点将会获得来自其它节点的值而且这个节点也只会赞同那个值(不会再提出自己的值了)。这也意味着Paxos算法不会阻塞即使一半的节点无法回复消息。

当然也可能发生是领导节点自己故障了。为了解决这个问题,Paoxs在给定的时间点不会只向一个领导节点授权。它允许任何节点(在领导节点故障时)成为领导者并协调事务。这也自然意味着在特定情况下至少存在一个节点能称为领导者。在上述情况下很可能存在两个领导者并且他们发送了不同的值。所以如何达成共识呢?为了在这样的设置下达成共识,Paxos介绍了两种机制:

给领导者指定顺序。这让每个节点能够区分当前领导者和旧的领导者,它阻止旧领导者(或许刚从旧的故障中恢复)扰乱已经达成的共识。

限制领导者对值的选择。一旦就某个值`达成了共识,Paxos强制将来的领导只能选择相同的值确保共识能延续下去。这一点是这样实现的-接收节点发送它赞同的最新的值和它收到的领导者的序列号(来实现上一点)。新的领导者可以从接受者发送的值中选择一个,万一没有任何接收节点发送值,领导者也可以选择自己的值。

一个节点被选为领导者并且选择序列号x和值v创建提议P1(x,v)。领导者把P1发送给接收者并等待大部分节点响应。

接收者一旦接收到提议P1(x,v)会做下面的事:

如果是接收者第一次收到提议而且它选择赞同,回复“赞同”-这是接收者的承诺,它将承诺拒绝将来所有小于x的提议请求。- 如果接收者已经赞同了提议:

比较x和接收者赞同的提议的最高序列号,称为P2(y,v2)

若x&lt;y,回应“拒绝”以及y的值- 若x&gt;y,回应“赞同”以及P2(y,v2)

- 如果大部分接收节点未能回应或者回应“拒绝”,领导者放弃这次协议并重新开始。

- 如果大部分接收节点回应“赞同”,领导者也会接受大部分节点接受的协议的值。领导者选择这些值的任意一个并发送<code>接受请求</code>以及提议序列号和值。

- 当接收者收到<code>接受请求</code>消息,它只在下面两种情况符合时发送“接受”信息,否则发送“拒绝”:

- 值和之前接受的提议中的任一值相同

- 序列号和接收者赞同的最高提议序列号相同

- 如果领导者没有从大部分节点那接收到“接受”消息,它会放弃这次提议重新开始。但是如果接收到了大部分的“接受”消息,深思熟虑后协议也可能被终止。作为优化,领导者要给其它节点发送“提交”信息。

正如人们所看到的,在故障容错上Paxos要优于2PC:

领导者故障了-另一位(新的)领导者可以通过发出自己的协议来接管协议。

原先的(故障)领导者恢复后-两个领导者可以同时存在,这归功于下面的规则:只赞同更高序列号的协议以及只提交以前接受的值。

Paxos也比3PC更加容错。具体地讲,Paxos是分区容错3PC不是。在3PC中,如果两个分区分别赞同一个值,当分区合并的时候,会留给你一个不一致的状态。在Paxos中,大部分情况下这不会发生。除非某个分区占绝大多数,否则不会达成共识。如果某个分区占绝大多数并达成一个共识,那值就需要被其它分区中的节点接受。

Paxos存在的一个问题是,两个领导者因为处于不同的分区导致不能互相观察,他们会努力向另一方投标,发布一个比先前协议有更高序列号的协议。这可能导致Paxos无法终止。最终这两个互相观察的领导者必须有一个需要做出退让。

本文提供了一个非常清晰的解释和证明:

我将努力提供一个清晰的总结:

Paxos算法的目的是帮助一些同等的节点就一个值达成一致的协议。Paxos保证如果一个节点相信一个值已经被多数节点赞同,多数节点就再也不会赞同其它值。这个协议被设计使得任何共识必须得到大部分节点的认可。任何将来试图达成共识的尝试,如果成功了也必须经过至少一个节点的认可。

因此,任何已经做出决定的节点必须和多数中的一个节点通信。协议保证节点将能从多数节点赞同的值得到收获。

下面讲述它是如何运作的:

假设我们有3个节点:A、B和C。A将提出一个值"Foo"

Paxos将分3个阶段执行。在每个阶段,必须有多数节点达成一致。

首先,我们来看准备阶段。节点A发送准备请求给A、B、C。Paxos根据序列号来担保。<code>准备请求</code>要求节点承诺:“我永远不会接受序列号比<code>准备请求</code>小的提议。”(被询问的节点)回复一个它们之前赞同的值(如果有的话)。(这一点非常重要):节点A必须回应它接收到的最高序列号的值。这一行为提供了保证:先前得到的赞同的值将被保留。

接下来我们进入到接受阶段。A发送一条接受请求给A,B和C。接受请求表示:“你接受Foo吗?”。如果伴随的序列号不小于节点先前已经承诺过的序列号或者是节点先前接受的请求的序列号,那么节点将接受新的值和序列号。

如果节点A从多数节点接收到的是“接受”,(Foo)这个值就被确定下来。Paxos循环也会终止不再询问新的值。

阶段三不是绝对必须的,但是如果在生产环境中实现Paxos算法,阶段三会是重要的优化。A收到多数节点“接受”消息后,它发送“已经确定值”消息给A,B和C。这条消息让所有节点知道已经选好了值,这会加快决定值的过程结束。

如果没有这个消息,其它节点将继续提出新的值来了解协议。在准备阶段,它们不断从预先约定的值中学习,一旦它们从协议得到结论,节点就会确认这个协议。

(为了便于理解)我掩盖了一些关键的问题:

1. 所有的序列号必须单调递增,而且不同节点序列号都不同。也就是说,A、B不能用同一个序列号k发送信息。协议要求所有发送的消息必须包含序列号。在准备阶段每个节点都要追踪它遇到的最高接受请求和它承诺的(序列号)最高的值。

2. 故障情况。一次Paxos循环中很可能失败,万一失败了,一个节点会用更高的序列号发起新的提议。

3. 终止情况。我所描述的Paxos版本不一定出现终止故障。对于正常的终止情况,(我描述的)Paxos算法还需要一些调整。

=========================================

译者介绍

adolphlwq,南京信息工程大学本科大四学生,对Docker充满兴趣,喜欢运动。现在正努力充电希望快速进步。

原文发布时间为:2015-09-04 

本文作者:adolphlwq 

本文来自云栖社区合作伙伴DockerOne,了解相关信息可以关注DockerOne。

原文标题:一篇文章带你了解Paxos算法

继续阅读