Paxos算法

Paxos是一致性协议的基础,侧重理论。处理实际问题的侧重点不同,衍生出其它各种改进版本协议

角色

  • Proposer 负责提出提案

  • Acceptor 负责对提案做出裁决(accept or no)

  • Learners 负责学习提案结果

现实中,一个进程可担任多个角色。

议案ID生成算法

在Google的Chubby论文中给出了这样一种方法:假设有n个proposer,每个编号为ir(0<=ir<n),proposal编号的任何值s都应该大于它已知的最大值,并且满足:

 s %n = ir    =>     s = m*n + ir

proposer已知的最大值来自两部分:proposer自己对编号自增后的值和接收到acceptor的拒绝后所得到的值。

例: 以3个proposer P1、P2、P3为例,开始m=0,编号分别为0,1,2。

1) P1提交的时候发现了P2已经提交,P2编号为1 >P1的0,
因此P1重新计算编号:new P1 = 1*3+1 = 4;

2) P3以编号2提交,发现小于P1的4,因此P3重新编号:new P3 = 1*3+2 = 5。

决议的发布

Acceptor集群中的每个成员都有可能同意该提案且只能批准一个提案,Proposer只有当收到一半以上的Acceptor成员同意了一个提案,就认为该提案被选定了,

acceptors需要将accept消息发送给learners的一个子集,然后由这些learners去通知所有learners。

但是由于消息传递的不确定性,可能会没有任何learner获得了决议批准的消息。当learners需要了解决议通过情况时,可以让一个proposer重新进行一次提案。注意一个learner可能兼任proposer。

两个阶段

prepare阶段

  1. Proposer选择一个提案编号N,然后向半数以上的Acceptor发送编号为N的Prepare请求。Pareper(N)
  2. 一个Acceptor收到一个编号为N的Prepare请求,
  • 如果小于它已经响应过的请求,则拒绝,不回应或回复error。
  • 若N大于该Acceptor已经响应过的所有Prepare请求的编号(maxN),那么它就会将它已经接受过(已经经过第二阶段accept的提案)的编号最大的提案(如果有的话,如果没有的话accept返回{pok,null,null})作为响应反馈给Proposer,同时该Acceptor承诺不再接受任何编号小于N的提案。

accept阶段

当一个proposer收到了半数以上Acceptor对其发出的编号为N的Prepare请求的响应,就进入批准阶段。它要向回复prepare请求的acceptors发送对[N,V]提案的accept请求。

V就是收到的响应中编号最大的提案的value(某个acceptor响应的它已经通过的{acceptN,acceptV}),如果响应中不包含任何提案,那么V就由Proposer自己决定

如果Acceptor收到一个针对编号为N的提案的Accept请求,

  • 只要该Acceptor没有对编号大于N的Prepare请求做出过响应,它就接受该提案。
  • 如果N小于Acceptor之前响应的prepare(N)请求,则拒绝,不回应或回复error(当proposer没有收到过半的回应,那么他会重新进入第一阶段,递增提案号,重新提出prepare请求)

在这里插入图片描述

拜占庭将军问题

拜占庭帝国就是5~15世纪的东罗马帝国,拜占庭即现在土耳其的伊斯坦布尔。我们可以想象,拜占庭军队有许多分支,驻扎在敌人城外,每一分支由各自的将军指挥。将军们智能靠通讯员进行通讯。在观察敌人以后,忠诚的将军们必须制订一个统一的行动计划——进攻或者撤退。然而,这些将军冢有叛徒,他们不希望忠诚的将军们能达成一致,因而影响统一行动计划的制订与传播。

问题是:将军们必须有一个协议,使所有忠诚的将军们能够达成一致,而且少数几个叛徒不能使忠诚的将军们作出错误的计划——使有些将军进攻而另一些将军撤退。

如果这11位将军中有间谍呢? 假设有9位忠诚的将军,5位判断进攻,4位判断撤退,还有2个间谍恶意判断撤退,虽然结果是错误的撤退,但这种情况完全是允许的。因为这11位将军依然保持着状态一致性。
1.11位将军进攻城池

2.同时进攻(议案、决议)、同时撤退(议案、决议)

3.不管撤退还是进攻,必须半数的将军统一意见才可以执行

4.将军里面有叛徒,会干扰决议生成

先后提议的场景

在这里插入图片描述
角色:

proposer:参谋1,参谋2

acceptor:将军1,将军2,将军3(决策者)

  1. 参谋1发起提议,派通信兵带信给3个将军,内容为(编号1);
  2. 3个将军收到参谋1的提议,由于之前还没有保存任何编号,因此把(编号1)保存下来,避免遗忘;同时让通信兵带信回去,内容为(ok);
  3. 参谋1收到至少2个将军的回复,再次派通信兵带信给3个将军,内容为(编号1,进攻时间1);
  4. 3个将军收到参谋1的时间,把(编号1,进攻时间1)保存下来,避免遗忘;同时让通信兵带信回去,内容为(Accepted);
  5. 参谋1收到至少2个将军的(Accepted)内容,确认进攻时间已经被大家接收;
  6. 参谋2发起提议,派通信兵带信给3个将军,内容为(编号2);
  7. 3个将军收到参谋2的提议,由于(编号2)比(编号1)大,因此把(编号2)保存下来,避免遗忘;又由于之前已经接受参谋1的提议,因此让通信兵带信回去,内容为(编号1,进攻时间1);
  8. 参谋2收到至少2个将军的回复,由于回复中带来了已接受的参谋1的提议内容,参谋2因此不再提出新的进攻时间,接受参谋1提出的时间;
交叉场景

在这里插入图片描述
proposer:参谋1,参谋2

acceptor:将军1,将军2,将军3(决策者)

  1. 参谋1发起提议,派通信兵带信给3个将军,内容为(编号1)
  2. 3个将军的情况如下: 将军1和将军2收到参谋1的提议,将军1和将军2把(编号1)记录下来,如果有其他参谋提出更小的编号,将被拒绝;同时让通信兵带信回去,内容为(ok);负责通知将军3的通信兵被抓,因此将军3没收到参谋1的提议;
  3. 参谋2在同一时间也发起了提议,派通信兵带信给3个将军,内容为(编号2);
  4. 3个将军的情况如下:
  • 将军2和将军3收到参谋2的提议,将军2和将军3把(编号2)记录下来,如果有其他参谋提出更小的编号,将被拒绝;同时让通信兵带信回去,内容为(ok);
  • 负责通知将军1的通信兵被抓,因此将军1没收到参谋2的提议;
  1. 参谋1收到至少2个将军的回复,再次派通信兵带信给有答复的2个将军,内容为(编号1,进攻时间1);
  2. 2个将军的情况如下:
  • 将军1收到了(编号1,进攻时间1),和自己保存的编号相同,因此把(编号1,进攻时间1)保存下来;同时让通信兵带信回去,内容为(Accepted);
  • 将军2收到了(编号1,进攻时间1),由于(编号1)小于已经保存的(编号2),因此让通信兵带信回去,内容为(Rejected,编号2);
  1. 参谋2收到至少2个将军的回复,再次派通信兵带信给有答复的2个将军,内容为(编号2,进攻时间2);
  2. 将军2和将军3收到了(编号2,进攻时间2),和自己保存的编号相同,因此把(编号2,进攻时间2)保存下来,同时让通信兵带信回去,内容为(Accepted);
  3. 参谋2收到至少2个将军的(Accepted)内容,确认进攻时间已经被多数派接受;
  4. 参谋1只收到了1个将军的(Accepted)内容,同时收到一个(Rejected,编号2);参谋1重新发起提议,派通信兵带信给3个将军,内容为(编号3);
  5. 3个将军的情况如下:
  • 将军1收到参谋1的提议,由于(编号3)大于之前保存的(编号1),因此把(编号3)保存下来;由于将军1已经接受参谋1前一次的提议,因此让通信兵带信回去,内容为(编号1,进攻时间1);
  • 将军2收到参谋1的提议,由于(编号3)大于之前保存的(编号2),因此把(编号3)保存下来;由于将军2已经接受参谋2的提议,因此让通信兵带信回去,内容为(编号2,进攻时间2);
  • 负责通知将军3的通信兵被抓,因此将军3没收到参谋1的提议;
  1. 参谋1收到了至少2个将军的回复,比较两个回复的编号大小,选择大编号对应的进攻时间作为最新的提议;参谋1再次派通信兵带信给有答复的2个将军,内容为(编号3,进攻时间2);
  2. 将军1和将军2收到了(编号3,进攻时间2),和自己保存的编号相同,因此保存(编号3,进攻时间2),同时让通信兵带信回去,内容为(Accepted);
  3. 参谋1收到了至少2个将军的(accepted)内容,确认进攻时间已经被多数派接受。

活锁问题

当一个proposer发现存在编号更大的提案时将终止提案。这意味着提出一个编号更大的提案会终止之前的提案过程。如果两个proposer在这种情况下都转而提出一个编号更大的提案,就可能陷入活锁,违背了Progress的要求。

解决方法:

  • 随机睡眠-重试,在被打回第一阶段再次发起PrepareRequest请求前加入随机等待时间。

如果一个proposer通过accpter返回的消息知道此时有更高编号的提案被提出时,该proposer静默一段时间,而不是马上提出更高的方案,静默期长短为一个提案从提出到被接受的大概时间长度即可,静默期过后,proposer重新提案。

  • 设置一个超时时间,到达超时时间后,不再接收PrepareRequest请求。
  • 在proposer中选举出一个leader,通过leader统一发出PrepareRequest和AcceptRequest。

在这里插入图片描述

异常情况——持久存储

在算法执行的过程中会产生很多的异常情况:proposer宕机,acceptor在接收proposal后宕机,proposer接收消息后宕机,acceptor在accept后宕机,learn宕机,存储失败,等等。

为保证paxos算法的正确性,proposer、aceptor、learn都实现持久存储,以做到server恢复后仍能正确参与paxos处理。

  • propose存储已提交的最大proposal编号、决议编号(instance id)。
  • acceptor存储已承诺(promise)的最大编号、已接受(accept)的最大编号和value、决议编号。
  • learn存储已学习过的决议和编号

一个Paxos集群,每个server同时担任proposer和acceptor,任何server都可以发起持久化redolog(即议案)的请求,

Basic-Paxos协议对每条redolog(即议案)的持久化都至少存在三次网络交互的延迟(1. 产生logID;2. prepare阶段;3. accept阶段)

1、首先要向所有的server查询当前最大logID,从多数派的应答结果中选择最大的logID,加1后作为执行本次Paxos Instance的唯一标识;

2、然后进入Paxos的prepare阶段,产生proposalID,并决定出要投票的redolog(即议案);

3、在accept阶段对prepare阶段产生的议案进行投票,得到多数派确认后返回成功。。

下面我们逐个分析每个阶段的必要性:

  1. 产生logID,由于Basic-Paxos并不假设一段时间内只有唯一的proposer,因此可能由集群内的任意server发起redolog同步,因此不能由单一server维护logID,而是需要进行分布式协商,为不同的redolog分配全局唯一且有序的logID。
  2. prepare阶段,上述一阶段的分布式协商logID并不能保证并发多个server分配得到得logID是唯一的,即会出现若干条不同的redolog在同一个Paxos Instance中投票的情况,而这正是Basic-Paxos协议的基本假设,因此需要执行prepare,以决定出这个Paxos Instance内的要进行投票的redolog(即议案)。如果执行prepare决定出的议案与server自己要投票的redolog内容不同,则需要重新产生logID。
  3. accept阶段,对prepare阶段决定出的议案进行投票,得到多数派确认后表示redolog同步成功,否则需要重新产生logID。

在这三个阶段中,根据Paxos协议的约束,server应答prepare消息和accept消息前都要持久化本地redolog,以避免重启后的行为与重启前自相矛盾。因此最终可以得到使用Basic-Paxos进行redolog同步的延迟包括了3次网络交互加2次写本地磁盘。并且在高并发的情况下,不同redolog可能被分配到相同的logID,最差可能会在accept阶段才会失败重试。

参考:
https://blog.csdn.net/changshaoshao/article/details/83343782
https://www.cnblogs.com/linbingdong/p/6253479.html

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值