介绍

Paxos算法是一个高容错性的分布式一致性算法。去年学习过Paxos算法,一直没将整理到博客。现在将经典Paxos算法相关内容整理到博客上。

经典Paxos算法本身也并不是太难理解,Lamport从期望的结果出发,通过增强条件一步步反推,最终发掘出可以保证了系统一致性的Paxos算法。如果能仔细体会到这其中一步步的反推,就会觉得Paxos是个很自然的东西了。

首先了解一下Paxos算法的作者Leslie Lamport (个人官网),由于对分布式领域的贡献获得2013年度图灵奖。

这里有一段Lamport获得图灵奖的访谈。他也在TeX基础上发明了LaTeX排版系统

Lamport在1982年与Robert Shostak和Marshall Pease一起发表了The Byzantine Generals Problem,也即著名的拜占庭将军问题。拜占庭将军问题揭示了在异步系统和不可靠通道上达到一致性是不可能的。

在1990年Lamport发表了论文The Part-Time Parliament,这就是Paxos的原始论文,当时没什么人能够懂这篇论文因为其用了讲故事的形式进行算法描述。Lamport拒绝主编要求修改论文的提案,因此论文最终被撤销。

在1997年MIT的Nancy Lynch发布了Paxos原文的修改版Revisiting the Paxos algorithm

这里补充一下,Nancy Lynch曾发表过论文Impossibility of Distributed Consensus with One Faulty Process引出FLP impossibility。

在1998年原论文最终被ACM Transactions on Computer Systems接受。

在2001年Lamport对原文重述,发表了Paxos Made Simple。此版论文简洁易懂,没有复杂数学公式,也成了大多数人学习Paxos的入口。

内容

由于Paxos Made Simple文章本身语言很简洁,本文也尽可能用简洁的表述来呈现Paxos算法。

可以说Paxos算法本身是基于期望的目标/结果的一步步反推并强化条件而得出的。

背景

有一些进程可以发起提案,一致性算法要做到以下几点:

  1. 所有提出的提案中最终只有一个会被选定。
  2. 如果提出任何提案,则就没有被选定的提案。
  3. 如果一个提案被选定,进程应该要能获取到被选定的提案。

对于一致性算法的安全性,需要满足以下3点:

  1. 只有被提出的提案才能被选定。
  2. 只有一个提案能够被选定。
  3. 任何进程认为选定的提案必须是真正被选定的提案。

Pxos算法的目标就是要能够做到以上3点。

这里需要说明什么叫选定:一种很好的判断是否选定的方式是:对提案表决的进程中大多数批准提案。所谓大多数,可以简单理解为超过一半,任意两个大多数的集合一定是有交集的。

角色

定义三种参与者:

Proposer 可以发起提案

Acceptor 可以对Proposer的提案批准/拒绝

Learner 获取最终被选定的提案

这三种角色只是逻辑上的一种区分,在具体实现完全可以一个进程充当多种角色。

推导

假设没有失败/消息丢失,只提出一个提案,也可以选出最终提案,这意味着:

P1: 一个Acceptor必须批准它收到的第一个提案。

满足P1是不够的,因为很可能最终没有提案被大多数Acceptor批准。(这不难想象)

所以P1加上一个提案需要被多数派批准才能选定意味着一个Acceptor必须可以批准不止一个提案,否则很可能产生不出一个被多数派批准的提案。

我们这里可以给每个提案分配一个唯一的自然数作为编号,将提案表示为<id, value>的形式(其中id为编号,value为提案本身),这样可以用来区分每个Acceptor批准的不同提案。于是我们可以说,如果值为某个value的提案被多数派批准后,则该提案被选定。

上面已经将提案的概念演化为由编号与提案表示的一个组合了,这样的话可以有多个<id, value>表示的提案被选定。但必须要保证,所有被选定的提案的value值必须是同一个,否则就违背了一致性算法的初衷了。这里引出:

P2: 如果一个值为v的提案被选定了,则所有被选定的具有更高编号的提案值也必须是v。

P2保证了上文提及的一致性算法的安全性的第2点,即只能有一个提案被选定。因为一个提案要被至少一个Acceptor批准才能能够选定,所以可以通过满足下面引出的P2a来满足P2:

P2a: 如果一个值为v的提案被选定了,则被任一Acceptor批准的具有更高编号的提案值也必须是v。

可以看出,P2a是P2根据一个提案被选定肯定是需要先经过被Acceptor批准出发而得出的强化条件,满足了P2a显然能够满足P2。

这里产生了一个问题,由于通信是异步的,某个Acceptor还没有收到任何提案时,可能系统已经产生了某个被选定的提案,此时这个Acceptor接收到了另一个Proposer发出的具有更高编号的提案,根据P1需要批准,这样的话就与P2a矛盾了。也就是说P2a与P1不兼容

P2a根据提案的被选定的上一步是需要被Acceptor批准出发,强化P2得出P2a。那么这里可以用类似思路,根据一个提案得先被Proposer提出才能被Acceptor批准,进一步强化p2a得出:

P2b: 如果一个值为v的提案被选定了,则之后任一Proposer产生的编号更高的填,value值也必须是v。

P2b可以看作是对Proposer提出提案作出要求,但P2b仍然是很难实现的。下面引出P2c:

P2c: 如果一个提案<n, v>被提出,则必须要存在一个多数Acceptor集S满足如下条件之一:
a) S中任一Acceptor都没有批准过编号小于n的提案。
b) S中Acceptor批准过的编号最大的小于n的提案的值为v。

这其中a)可以看作是初始的边界条件。

只要维持P2c的不变性就可以满足P2b。接下来就通过第二数学归纳法简单证明一下,假定一个提案<n, v>被选定,通过维持P2c可以满足P2b:

假定此时一个Proposer要提出<m, v'>的提案的话:

  1. 当m=n+1时,根据P2c的b),满足存在一个多数Acceptor集S'满足批准过编号最大的小于n的提案值为v'。那么设批准<n, v>的多数Acceptor集为S,则S与S'必然存在交集。对于这些交集Acceptor而言,“批准过的编号最大的小于n的提案”便是<n, v>,则推得v'=v成立。所以在m=n+1时,提出的提案<m, v'> = <n+1, v'> = <n+1, v>。

  2. 假设在编号在[n+1, m-1]的提出的提案的值都为v,需要证明P2b也就是此时v'=v:根据P2c,v'应该等于某个多数Acceptor集S'中Acceptor批准的编号最大的提案的值。

    1). 假设S'中Acceptor批准过编号最大的提案的编号在[n+1, m-1]范围,则根据归纳假设,提案值显然为v。

    2). 假设S'中Acceptor批准过编号最大的提案的编号为n,则此时S'必然与批准<n, v>的那个多数Acceptor集S有交集,而S的所有Acceptor都批准了<n, v>,由此可得提案值为v。

由此得证在满足P2c的条件下,P2b也成立。

算法过程

生成提案

上面P2c中我们可以看出,对于一个要提出提案的Proposer而言,最重要的就是要知道Acceptor已经批准过编号最大的提案是什么。

如果某个Proposer想要提出编号为n的协议,对于Acceptor而言,告诉Proposer它们已经批准过的编号小于n中编号最大的提案是很容易的。但是编号小于n中其余剩下还未被批准的提案(可能是还Acceptor还未收到或者还未响应)后续可能会对结果造成难以预期的影响。Paxos算法中为了避免这种不确定因素,需要Acceptor保证不会再批准任何编号小于n的提案。

这里引出提案生成法:

  1. Proposer选择一个自己的提案编号n,向Acceptor集合(或者一个多数集可)发送Prepare请求,要求Acceptor回应:

    • 承诺保证不再批准任何编号小于n的提案。
    • 如果已经批准了任何提案,告诉Proposer已经批准的最大的小于n的提案值。
  2. 如果Proposer收到了多数派Acceptor响应,则可以根据P2c产生提案。即如果这些Acceptor都没有批准过任何提案,则可以随意选定一个提案值v,否则选择这些响应Acceptor中具有最大编号的提案值。

此时Proposer可以向Acceptor集合(或者一个多数集也可)发送Accept请求请求其批准提案。

批准提案

Acceptor可以在任何时候都可以响应Prepare请求。在不违背已经响应过的承诺的前提下,可以响应Accept请求批准提案。

下面引出对于Acceptor如何对提案的处理逻辑:

P1a: 一个Acceptor批准某个提案当且仅当它没有响应过具有更高编号的Prepare请求。

P1a是对P1的一种强化。

优化

对于Prepare请求,有一个优化点:如果一个Acceptor已经响应了编号为n的Prepare请求,则可以忽略后续任何编号小于n的Prepare请求。因为根据对编号为n的Prepare请求的承诺,Acceptor是不会批准编号小于n的提案,自然没必要响应其Prepare请求了。所以其实对Acceptor而言,只需要维护其响应过的Prepare请求中的最大编号以及批准过的提案中的最大编号即可。一个Proposer只要保证不会产生重复编号的提案,可以随意丢弃提出的提案。

完整的算法描述

综合上述的提交提案与批准提案,下面给出完整的算法描述。

阶段1:

  1. Proposer选择一个提案编号n,向Acceptor的一个多数派集合发送Prepare请求。
  2. Acceptor如果收到编号为n的Prepare请求,如果已经响应过编号大于n的Prepare请求,则可以选择忽略该Prepare请求。否则承诺不会批准编号小于n的提案,并且如果已经有批准过编号小于n的提案的话需要回复给Proposer。

阶段2:

  1. 如果Proposer收到了超过半数的Prepare请求响应,则会根据响应确定提案值,向Acceptor集合(要足以形成多数派,可以不是原先那些)发送Accept请求。
  2. 收到Accept请求的Acceptor只要没有响应过编号更大的Prepare请求,就可以通过提案。

参考资料

The Part-Time Parliament

Paxos Made Simple

《从Paxos到ZooKeeper》

http://www.cs.yale.edu/homes/aspnes/pinewiki/Paxos.html

经典Paxos算法笔记的更多相关文章

  1. Zookeeper笔记(二)Paxos算法与Zookeeper的工作原理

    Zookeeper 分布式服务框架是 Apache Hadoop 的一个子项目, 它主要是用来解决分布式应用中经常遇到的一些数据管理问题,如:统一命名服务.状态同步服务.集群管理.分布式应用配置项的管 ...

  2. 分布式理论之一:Paxos算法的通俗理解

    维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 LaTeX 中的"La",此人现在在微软研究院)于1990年提出的一种基于消息传递且具有高度容错特性 ...

  3. 一步一步理解Paxos算法

    一步一步理解Paxos算法 背景 Paxos 算法是Lamport于1990年提出的一种基于消息传递的一致性算法.由于算法难以理解起初并没有引起人们的重视,使Lamport在八年后重新发表到 TOCS ...

  4. Paxos算法小结

    转自不正直的绅士,因百度空间迁移,无法注明出处,我从其google搜索引擎中的cache进行的copy. 不正直的绅士 是跟我一起工作过的非常有才的一个青年才俊. Paxos的使用非常广泛.sanlo ...

  5. 底层算法系列:Paxos算法

    关于算法,面太广.本系列只研究实际应用中遇到的核心算法.了解这些算法和应用,对java码农进阶是很有必要的. 对于Paxos学习论证过程中,证实一句话:有史以来学习paxos最好的地方wiki:Pax ...

  6. 从分布式一致性到共识机制(一)Paxos算法

    从分布式系统的CAP理论出发,关注分布式一致性,以及区块链的共识问题及解决. 区块链首先是一个大规模分布式系统,共识问题本质就是分布式系统的一致性问题,但是又有很大的不同.工程开发中,认为系统中存在故 ...

  7. Paxos 算法 :消息传递一致性

    网络上有很多关于优秀的关于Paxos 算法的文章,我下面进行整理搜集一下: 分布式理论之一:Paxos算法的通俗理解 维基的简介:Paxos算法是莱斯利·兰伯特(Leslie Lamport,就是 L ...

  8. [从Paxos到ZooKeeper][分布式一致性原理与实践]<二>一致性协议[Paxos算法]

    Overview 在<一>有介绍到,一个分布式系统的架构设计,往往会在系统的可用性和数据一致性之间进行反复的权衡,于是产生了一系列的一致性协议. 为解决分布式一致性问题,在长期的探索过程中 ...

  9. 算法笔记(c++)--01背包问题

    算法笔记(c++)--经典01背包问题 算法解释起来太抽象了.也不是很好理解,最好的办法就是一步步写出来. 背包问题的核心在于m[i][j]=max(m[i-1][j],m[i-1][j-w[i]]+ ...

随机推荐

  1. .NET中OpenFileDialog使用报线程错误的解决方法

    昨天,在做一个NPOI读取的小demo的时候,使用OpenFileDialog打开文件,最开始的写法,直接在按钮点击事件中写,会报错,代码如下: OpenFileDialog ofd = new Op ...

  2. php json_encode在CI框架中的使用细节

    这个错误的造成原因是加载类类库,转换成json格式的时候不熟悉CI框架的规定导致的,CI框架中规定在将数据转换成json格式的时候需要将类库小写,当然了,调用的时候必须保证有这个类库,且可以在对应的文 ...

  3. MySQL的相关应用

    本文主要介绍数据库中MySQL的基础知识,包括数据插入.数据更新.数据删除.数据查询.函数.CASE语句.表连接.子查询. 一.数据插入(insert 语句) 首先我创建一个表,如下: 接着,进行数据 ...

  4. Idea无法运行Maven项目

    导入项目到tomcat的时候要选择Arifact 如果maven项目没有这个选项, <groupId>com.bihang</groupId> <artifactId&g ...

  5. java锁的简化

    java使用单独的锁对象的代码展示 private Lock bankLock = new ReentrantLock(); //因为sufficientFunds是锁创建的条件所以称其为条件对象也叫 ...

  6. Linux CentOS Nginx安装配置

    Nginx("engine x")是一款是由俄罗斯的程序设计师Igor Sysoev所开发高性能的 Web和 反向代理 服务器,也是一个 IMAP/POP3/SMTP 代理服务器. ...

  7. Mybatis插件开发

    前面几篇文章介绍了Mybtis中四个重要的对象,其中提到它们都是在Configuration中被创建的,我们一起看一下创建四大对象的方法,代码如下所示: public ParameterHandler ...

  8. 如何启动一个Vue2.x项目

    1. cd到工作目录2. npm init -y3. 先查看有没有安装全局的vue-cli,:vue-V,没有的话安装一下:npm install vue-cli4. 创建项目: vue init w ...

  9. JSP内置对象——page对象

    观察可发现,这里面的方法,就是Object这个类下的一些方法,下面进行一个简单的演示,比如“toString()”方法: 运行结果: 这时候看到了一个“org.apache.jsp.page_jsp@ ...

  10. python 多进程、多线程

    1.多线程: 下面讲一个简单用法,这个模块比较简单,但是实际使用中会遇到很多坑 from multiprocessing import process def go(s): print "主 ...