底层算法系列:Paxos算法

关于算法,面太广。本系列只研究实际应用中遇到的核心算法。了解这些算法和应用,对java码农进阶是很有必要的。

对于Paxos学习论证过程中,证实一句话:有史以来学习paxos最好的地方wiki:Paxos (computer science)

目录

1.背景

2.Paxos算法

3.Muti-Paxos算法

4.Muti-Paxos在google chubby中的应用

===============正文分割线============================

一、背景

Paxos 协议是一个解决分布式系统中,多个节点之间就某个值(提案)达成一致(决议)的通信协议。但Paxos算法晦涩难懂,原版论文也是让人难以理解。故有了本文,希望给大家提供一点思路。

二、Paxos算法

2.1角色(核心就3个角色)

Client:客户端,发起请求并等待返回。
Proposer:提议发起者,处理客户端请求,将客户端的请求发送到集群中,以便决定这个值是否可以被批准。
Acceptor:提议批准者,负责处理接收到的提议,他们的回复就是一次投票。会存储一些状态来决定是否接收一个值。
Learner:当有同一个value的协议被超过一半的Acceptor采纳并发送消息给Learner时,Learner采纳该协议值。
Leader:一个特殊的Proposer。

2.2 Basic-Paxos算法

核心实现Paxos Instance主要包括两个阶段:

准备阶段(prepare phase)和提议阶段(accept phase)。细化为4个小阶段,wiki上是这样描述的:

底层算法系列:Paxos算法

简单来说,Basic Paxos 是一个经典两阶段提交(2PC)

第一阶段:

  • 1a prepare 准备: proposer向acceptor提出一个协议,这里的协议就是期望的“一致性内容”
  • 1a promise 承诺: acceptor承诺只接收最大协议号的协议(包括prepare和accept),并拒绝比当前协议号N小的协议,回复proposer之前接收的所有协议值。如果当前协议号N比之前都小,那么回复拒绝。

第二阶段:

  • 2a Accept Request 发起“accept”请求:proposer收到acceptor反馈的足够的承诺后,给协议设最大值,如果没回复,随便设置一个值。发送"accept"请求给选定值的acceptors.
  • 2b Accepted:acceptor接受协议(该acceptor之前没有承诺过大于该协议号的协议),并通知给proposer和learner.

    配上wiki流程图如下:

底层算法系列:Paxos算法

其中prepare阶段的作用,如下图所示:

底层算法系列:Paxos算法

1.S1首先发起accept(1,red),并在S1,S2和S3达成多数派,red在S1,S2,S3上持久化
2.随后S5发起accept(5,blue),在S3,S4和S5达成多数派,blue在S3,S4和S5持久化
4.最后的结果是,S1和S2的值是red,而S3,S4和S5的值是blue,没有达成一致。
所以两阶段必不可少,Prepare阶段的作用是阻塞旧的提议,并且返回已经接收到的acceptedProposal。

解决方案:

1.将提议进行排序,可以为每个提议赋予一个唯一的ID,规定这个ID越大越新,很明显(5,blue)和(1,red),5比1大,所以保留blue

2.采用两阶段方法,拒绝旧提议。

2.3 Muti-Paxos算法

个人理解Muti-Paxos和Basic Paxos最大的区别在于:

1.多个实例

2.有唯一的Leader(一个特殊的Proposer),并由这个Leader提交value给各Acceptor进行表决。此时Prepare的过程就可以被跳过。

很多文章有误解说Muti-Paxos是一阶段提交,那是仅限于leader稳定时。刚选出来一个新的leader时,依然是二阶段提交如下图:

底层算法系列:Paxos算法

如果leader稳定,不需要prepare和promise步骤,如下图:

底层算法系列:Paxos算法

Multi Paxos中leader用于避免活锁(例如1个leader,4个Proposer,2个提议A,2个提议B不能达成一致,导致活锁),但leader的存在会带来其他问题,一是如何选举和保持唯一leader(虽然无leader或多leader不影响一致性,但影响决议进程progress),二是充当leader的节点会承担更多压力,如何均衡节点的负载。Mencius[1]提出节点轮流担任leader,以达到均衡负载的目的;租约(lease)可以帮助实现唯一leader,但leader故障情况下可导致服务短期不可用。

2.4 Muti-Paxos在google chubby中的应用

Google Chubby是一个高可用分布式锁服务,被设计成一个需要访问中心化节点的分布式锁服务。本文只分析chubby服务端的实现。

Chubby服务端的基本架构大致分为三层

  ① 最底层是容错日志系统(Fault-Tolerant Log),通过Paxos算法能够保证集群所有机器上的日志完全一致,同时具备较好的容错性。

  ② 日志层之上是Key-Value类型的容错数据库(Fault-Tolerant DB),其通过下层的日志来保证一致性和容错性。

  ③ 存储层之上的就是Chubby对外提供的分布式锁服务和小文件存储服务。

底层算法系列:Paxos算法

Paxos算法用于保证集群内各个副本节点的日志能够保持一致,Chubby事务日志(Transaction Log)中的每一个Value对应Paxos算法中的一个Instance(对应Proposer),由于Chubby需要对外提供不断的服务,因此事务日志会无限增长,于是在整个Chubby运行过程中,会存在多个Paxos Instance,同时,Chubby会为每个Paxos Instance都按序分配一个全局唯一的Instance编号,并将其顺序写入到事务日志中去。

  在Paxos中,每一个Paxos Instance都需要进行一轮或多轮的Prepare->Promise->Propose->Accept这样完整的二阶段请求过程来完成对一个提议值的选定,为了保证正确性的前提下尽可能地提高算法运行性能,可以让多个Instance共用一套序号分配机制,并将Prepare->Promise合并为一个阶段。具体做法如下:

  ① 当某个副本节点通过选举成为Master后,就会使用新分配的编号N来广播一个Prepare消息,该Prepare消息会被所有未达成一致的Instance和目前还未开始的Instance共用。

  ② 当Acceptor接收到Prepare消息后,必须对多个Instance同时做出回应,这通常可以通过将反馈信息封装在一个数据包中来实现,假设最多允许K个Instance同时进行提议值的选定,那么:

  -当前之多存在K个未达成一致的Instance,将这些未决的Instance各自最后接受的提议值封装进一个数据包,并作为Promise消息返回。

  -同时,判断N是否大于当前Acceptor的highestPromisedNum值(当前已经接受的最大的提议编号值),如果大于,那么就标记这些未决Instance和所有未来的Instance的highestPromisedNum的值为N,这样,这些未决Instance和所有未来Instance都不能再接受任何编号小于N的提议。

  ③ Master对所有未决Instance和所有未来Instance分别执行Propose->Accept阶段的处理,如果Master能够一直稳定运行的话,那么在接下来的算法运行过程中,就不再需要进行Prepare->Promise处理了。但是,一旦Master发现Acceptor返回了一个Reject消息,说明集群中存在另一个Master并且试图使用更大的提议编号发送了Prepare消息,此时,当前Master就需要重新分配新的提议编号并再次进行Prepare->Promise阶段的处理。

  可见chubby就是一个典型的Muti-Paxos算法应用,在Master稳定运行的情况下,只需要使用同一个编号来依次执行每一个Instance的Promise->Accept阶段处理。

  

三、总结

Paxos算法的变种还有很多Cheap Paxos、Fast Paxos等等,本文介绍了使用最广的Muti-Paxos算法。希望能够带给大家一点分布式一致性算法的入门灵感和思想。

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

参考:

1.paxos的wiki:Paxos (computer science)

2.csdn博客:一步一步理解Paxos算法

3.书:《从Paxos到Zookeeper》

4.论文:《Time-Clocks-and-the-Ordering-of-Events-in-a-Distributed-System》

上一篇:博客园主题美化


下一篇:PAXOS/RAFT理解