Paxos 学习总结

近期学习了分布式领域的重要算法Paxos,这里罗列下关键点当作总结。自己水平有限,难免存在谬误,恳请读者指正。本篇不包含Paxos的基本理论介绍。Paxos基础能够參考以下的学习资料章节。

1 Paxos图示

绘图总结了原始的Paxos算法,主要来源于Paxos Made Simple。

没有Leader也没有不论什么优化过程,而且把Proposer/Acceptor/Learners分开表示。

这仅仅是为了更easy梳理出三者之间的关系。我们仅仅要知道实际Paxosproject中会把这三者重叠处理就能够了。

Paxos 学习总结

Phase 1注:

1 Proposer向一个多数派Acceptor发送阶段1a消息时。所谓多数派可能是所有Acceptor也可能仅仅是一个大于50%的Acceptor集合。

2 阶段1可能产生多个Master,在若干Master进入阶段2后,仍有可能有新的搅局者进入阶段1,Paxos的各个阶段是能够互相重叠的。也就是说可能出现某个Proposer处于阶段1而还有一个Proposer处于阶段2中,且这两个Proposer也全然有可能由于訪问同一个Acceptor而互相影响。

处于阶段1和阶段2的Proposer之间的作用就是通过图中的虚线完毕的:一个处于阶段2的Proposer由于给某个Acceptor的(num value)赋值导致还有一个处于阶段1的Proposer不得不放弃自己的取值转而使用这个阶段2的Proposer的取值。

Phase 2注:

Q:假设多个Master已经发送2a消息,是不是未来的确定性取值一定在这几个Proposer中产生?

        A:当然不是。由于随时可能会有新搅局者在此时增加阶段1,如果x(3) y(5) 两个处于阶段2的Proposer(3,5是其n值)已经发出2a阶段消息,但龟速网络导致x的包在路上耽误了。而y的包也仅仅到达了1个Acceptor,其它的包在路上。此时一个新的搅局者z增加阶段1,发送了更大的n(如果是6)。由于n大于Max(Max是5,6>5成立)所以它迅速获得资格进入阶段2并使用自己的值提交Acceptor。此后即使x和y的龟速消息再到达也无济于事了。由于Acceptor的Max已经被z的阶段1消息抬高了。x和y的阶段2a消息会被Acceptor丢弃。当然了xy战胜z的概率还是非常大的,由于仅仅要xy的2a阶段消息能在z的阶段a1阶段到达Acceptor前,抢占一半以上的Acceptor,就能够了。由于这样就有一半多的Acceptor会阻挡掉z的阶段1a消息(作用路径是图中虚线),迫使z无法使用自己的值。而仅仅能接受y已经设的值。

Phase 3注:

1 学习过程是可能会乱序的。所以必须依照id(也就是图中Phase3的num)递增顺序学习。

2 阶段3的学习过程,消息传递时有多种选择,比方简单的多对多,m个Acceptor和n个Learner建立m*n条消息链路,然后发送消息。

或者多对一对多。即全部的Acceptor将消息发送给一个固定的Learner,然后这个Learner再广播给全部其它Learner。

可參考<<Paxos Made Simple>> 2.4节

2 学习资料

我认为读论文大概是学习Paxos的最好的方式了,读不懂怎么办?一篇一篇换着读。以下4篇论文从不同的方面论述了Paxos原理及应用场景。内容虽有反复但也有非常大互补性,细致阅读这4篇基本能够对Paxos有全面的理解。我认为作为最早发表Paxos算法的<<The
Part-Time Parliament>>
反而能够略过不读,或者在全面理解Paxos后再当作历史读物看看。

1 <<Paxos Made Simple>>(原文中文)

这是Lamport 2001年写的基本上就是<<The Part-Time Parliament>>的简化版本号。我认为这篇比較通俗易懂。同一时候中译文质量也非常高并且关键位置译者加入了一些凝视有助于理解。在这里面Lamport强调的是Paxos的两阶段运行过程,最后的Phase 3没有被当作独立的阶段(Lamport用独立章节2.3介绍了Phase3),只是在其它文献中很多其它把Paxos看作3阶段的过程,第3阶段也就是学习阶段。

个人认为3阶段更方便理解所以在作图的时候分为三阶段了。

2 <<The Chubby lock service for loosely-coupled distributed systems>>(原文中文)

文章作者就是提出“世上仅仅有一种一致性算法,那就是Paxos”的Mike Burrows。

他在Google也是Paxos方面的先驱。而这篇Chubby也被以下Google自家的<<Paxos Made Live>>作为第一參考文献来引用。这篇Googleproject实践经验主要介绍了Paxos在project中的应用。Paxos的两大用途之中的一个就是分布式锁,Google使用Paxos完毕了其内部的分布式锁服务Chubby。

这篇文章环绕3个主题:1什么是分布式锁 2Paxos怎样作为分布式锁来使用 3project实践中的各种困难怎样克服。同一时候我们也能从这篇里寻觅到GFS和Bigtable是怎样与Chubby配合的。

3 <<Paxos Made Live>>(原文中文)

这篇应该算是Google对于其内部使用Paxos的总结,相比于Chubby那篇总结性更强,同一时候对于Paxos算法本身也有一定介绍 。本篇文章在Chubby篇之后写成。其參考文档里的第一篇就是那篇Chubby。本文非常适合作为学习完Paxos原理后的第一篇文章来阅读。

通过project师视角描写叙述的Paxos更easy被project师理解(以后看到不论什么Engineering Perspective的字样要注意了,非常可能会有惊喜哦)。比方在解释为什么实现一个Paxos系统如此困难时,他们是这么说的“尽管Paxos算法本身用一页伪代码就能够描写叙述下来,可是我们的完整实现包括了数千行C++代码。代码膨胀并不简单地是由于我们採用了C++来代替伪代码。也不是由于我们代码风格的繁琐。将该算法转换为一个实际的,产品级系统须要引入非常多的features和优化(有些是已经发表过的研究成果。有些不是)。”。对于Paxos的“在某个值上达成一致”这种使用方法,它也明白介绍成“在我们的系统中,须要在‘(多副本)日志中的下一条记录是哪条’上达成一致。”。这种文章读起来真是酣畅淋漓!

4 <<Consensus on Transaction Commit>>(原文中文)

两位图灵奖作者合著的文章必须不能错过。这篇同一时候由两位理论创始人介绍了自家的2PC和Paxos,然后提出了一个2PC和Paxos杂交出的Paxos提交算法。

通过正常情况和异常情况的性能对照,得出结论“两阶段提交实际上与仅仅有一个协调者Paxos提交是同构的”。

这篇文章能够作为学习2PC和Paxos的一篇不错的文章。

5 <<布式锁服务>>(链接)

这篇要比上面4篇来头小多了,是国内大学生论文性的文章,作者的团队基本实现了一个简化版的Chubby,可是由于文章来源于实践还是非常有意思,非常值得一读。

3 Paxos实例与Mutl-Paxos

        一个Paxos实例就是一次完整的Paxos执行过程。

即完整的执行一遍Phase1~3(如果没有优化),向Paxos提交一个value值就意味着在提交该value值时会启动一个Paxos执行实例。实际系统会以Paxos为基础来实如今一系列value值上的一致性,比方把一个日志文件,逐条的分发到一个多机的备份环境,每次分发一条日志可能就是执行一次Paxos实例。多个Paxos实例连续执行也就是Mutl-Paxos。Mutl-Paxos有几种优化方法。能够參考<<Paxos Made Live>>
4.2及5.2节。

4 编号n的选择

        Proposer须要选出一个递增的唯一序列号,有一种很形象直观的方法:在一个具有n个副本的系统中,首先为每一个副本r分配一个在0和n-1之间的标识符Ir。副本r能够选择一个比它全部已知的序列号大的最小s作为序号,同一时候保证s mod n=Ir。举例来说,在一个5副本的Paxos系统里,能够为副本1制作一个序列号队列0,5,10。15...为副本2制作好序列号队列1。6,11,16...以此类推。当Proposer须要增大序列号时,即从自己的队列里顺序取出一个就可以,这样就保证了每一个Proposer取出的都是全局最大并且与别人不反复的编号了。可參考<<Paxos
Made Live>> 4.1节

5 Paxos优化

这里优化的基础和原始Paxos算法有微小差异。这里如果全部的Proposer是由一个固定的Leader来发起请求的。选出一个Leader来作为唯一的提案提出者能够防止活锁,活锁是因为不断的新Proposer提出更高编号的阶段1a请求而导致每一个Proposer完毕阶段2a的消息。

关于Leader可參考<<Paxos Made Simple>> 2.4节

1 能够通过让Leader仅给半数以上的Acceptor发送阶段2a消息来降低正常情况下的消息数。Leader仅仅要从半数以上的Acceptor接受到阶段2b消息,就能够知道v已经被选定。假设未收到足够多的阶段2b消息。再向其余的Acceptor发送阶段2a消息。这样的方法可參考<<Consensus on Transaction Commit>> 4.1节

2 能够将多个Paxos实例串联起来以降低消息数目。

假设在多个实例运行中,协调者(也就是Leader)没有变化。那么阶段1a 1b的消息能够被忽略。

为了从这个优化中获益,Muti-Paxos算法会设计非常久才会选举一次Leader(这事实上就是Fast Paxos的基本思想了,事实上这也比較符合直觉,选举Leader事实上不是保持一致性的主要工作。而是为了应对异常而已。在非常多实现里。选举Leader这一步往往是被简化的)。这样的方法可參考<<Paxos Made Simple>> 4.2节

        3 Acceptor在选定v后直接广播给learner,而不是先发送给Leader。再由Leader转发给Learner。这样以额外消息数为代价将阶段3的消息延迟消除,与Leader类似,在这些进程收到来自半数以上的acceptor的阶段2b消息后。就能够获知被选定的value值了。这样的方法可參考<<Consensus on Transaction Commit>> 4.1节

6 Paxos使用方法

        我认为<<大规模分布式存储系统>>中的说法比較准确的,Paxos有两种使用方法:

1 实现全局的锁服务或者命名和配置服务,比如Google Chubby以及Apache Zookeeper(Zookeeper使用了Zab协议,其作者觉得这不是Paxos。可參考<<a simple totally ordered broadcast protocol>>,可是通过分析Zookeeper的工作方式也有人觉得Zab是Paxos的一种简化形式)。

2 将用户数据拷贝到多个数据中心,比如Google Megastore以及Google Spanner。

对于用途2相对好理解。由于原始Paxos演示样例就展示了复制数据到多备份机的功能。对于用途1。能够阅读Chubby论文来学习:首先要明确Paxos分布式锁是建议性锁而单机的类Mutex是强制锁,作为建议性锁要求使用方在使用资源前主动询问锁的状况,建议锁和资源在物理上是分离的。防君子不防小人。

我们如果建议性锁与Mutex一样,会有一个类似id的身份标识。

分布式锁的id不是"123abc"这种无意义标识符。而是高大上的Unix文件路径格式。从"123abc"变成"/123/abc",这样id变得很直观。由于能够通过id直接表示出锁是哪个组哪个应用的哪个功能的。这种名字"/projectX/groupA/add"明显比"123abc"要直白了很多。

这种路径同一时候使得锁具备了层级父子关系也很easy引入很多其它特性。要明确这种表示法其本质上与Unix的文件系统关系不大,由于"/"全然能够用别的符号比方“|”取代,比方"|projectX|groupA|app",写成反斜线全然是为了照应程序猿的直觉,降低学习成本(Chubby
论文是这么说的“由于Chubby的名字空间结构类似于文件系统......同一时候也减少了培训Chubby用户的难度。”)。所谓锁的id表示法更雅观的名字应该叫命名空间,这类似于编程语言的命名空间:通过层级关系终于定位一个类/变量的方法。

看完了命名空间,那Paxos到底怎样当作分布式锁使用呢?Paxos的基本用途就是使得多台机器在一个确定性取值上达成一致。如果如今有一个5备份机的Paxos环境,我们就叫它5Paxos。有一个外部项目Xapp要使用5Paxos作为分布式锁,Xapp要锁住自己的某个服务http://www.xxx.com/make-id。它须要先向5Paxos发送一个lock值。和自己申请的路径"/Project/Xapp/make-id"。

这样5Paxos就在这个确定性取值(“/Project/Xapp/make-id”是lock还是unlock)上达成一致。当前的一致就是lock。仅仅有当Xapp再发送一个unlock后。5Paxos在新的取值unlock上又一次达成一致。由于是建议性锁,所以Xapp内部不论什么要使用这个make-id接口的服务必须在使用前主动检查一下5Paxos存储的"/Project/Xapp/make-id"锁定状态是lock还是unlock。并遵守“在unlock时不訪问这个服务”的君子协定。这样锁的作用就终于达成了。而作为分布式锁的最大长处就是高可用性,5Paxos的5台备份机中坏掉2台全然不影响这个锁的正常工作。

上一篇:Python之实现一个简易计算器


下一篇:C++ ABI之名字改变,编译器生成符号研究(以Qt为例)