Loading [MathJax]/jax/output/CommonHTML/config.js
前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >专栏 >分布式理论:深入浅出Paxos算法

分布式理论:深入浅出Paxos算法

作者头像
Java高级架构
发布于 2019-01-02 09:52:50
发布于 2019-01-02 09:52:50
9160
举报
文章被收录于专栏:JAVA高级架构JAVA高级架构

前言

Paxos算法是用来解决分布式系统中,如何就某个值达成一致的算法。它晦涩难懂的程度完全可以跟它的重要程度相匹敌。目前关于paxos算法的介绍已经非常多,但却很少有人能对P2c提出自己的见解,大多数是和稀泥式的人云亦云,但我觉着应该旗帜鲜明的亮出自己的观点,大家一起讨论,才能学到东西。希望我能将自己的观点表述清楚。

我个人认为理解Paxos有两个关键:

  1. 为什么要对提案进行顺序编号(或者说更大的编号意味着什么)。
  2. 为什么Promise能保证一致性(答案隐含在第1点中)

一致性问题

假设有一组服务器保存了用户的余额,初始是100块,现在用户提交了两个订单,一个订单是消费10元,一个订单是充值50元。由于网络错误和延迟等原因,导致一部分服务器只收到了第一个订单(余额更新为90元),一部分服务器只收到了第二个订单(余额更新为150元),还有一部分服务器两个订单都接收到了(余额更新为140元),这三者无法就最终余额达成一致。这就是一致性问题。

一致性算法并不保证所有提出的值都是正确的(这可能是安全管理员的职责)。我们假设所有提交的值都是正确的,算法需要负责的使对到底该选哪个值做出决策,并使决策的结果被所有参与者获悉。

在正式开始介绍Paxos所面临的难题前,为了表述方便,先提一下Paxos算法中的三个角色,后面会比较频繁的用到:

  1. Proposer:议案发起者。
  2. Acceptor:决策者,可以批准议案。
  3. Learner:最终决策的学习者。

我们虚拟一个一致性问题的场景:有一个用户小绿,现在要对他的姓氏信息进行修改,此时有多个不同的议案被提出,如何就最终的结果达成一致。

首先看一下下面这种最简单的情况:A1接受了Pa的议案“赵”,A2和A3接受了Pb的议案“钱”,那么最终小绿应该姓什么?

答案很简单:超过半数的的议案就是最终的选定值。小绿应该姓“钱”!在议案提交后,Pa和Pb只要查询一下小绿姓氏,很容易就能查到 “钱”的数量超过半数,因此Pb的议案将会返回“成功”,Pa的议案将会返回“失败”。

P0. 当集群中,超过半数的Acceptor接受了一个议案,那我们就可以说这个议案被选定了(Chosen)。

P0已经是一个完备的一致性算法,保证了P0也就解决了一致性问题。但是P0的实用性不佳,一个议案想被半数以上的Acceptor接受是一件极其困难的事情!

看下面这种情况:A1,A2,A3分别接受了“赵”,“钱”,“孙”,结果没有任何一个议案形成多数派,所有的议案都将返回“失败”。议案的数量越多,那议案被选定的概率就越低,这显然是没法容忍的。

要解决这个问题,必须允许一个Acceptor接受多个议案,后接受的议案可以覆盖掉之前接受的议案。

如下图所示, A1已经接受了“赵”,A2已经接受了“钱”,此时Pc提出了“孙”,并被A1,A2,A3接受,这样就解决了无法形成多数派的问题。

但现在又会面临下图中的新问题:A1,A2,A3已经接受了“赵”,此时我们认为“赵”是被选定的,但此时偏偏Pb和Pc不识时务,Pb向A2提出了“钱”,Pc向A3提出了“孙”。这样就从一致性状态,又回到了不一致的状态…这显然破坏了一致性。

Paxos就是在上述背景下产生的,Paxos要实现的目标的是:

  1. 一次选举必须要选定一个议案(不能出现所有议案都被拒绝的情况)
  2. 一次选举必须只选定一个议案(不能出现两个议案有不同的值,却都被选定的情况)

Paxos算法的推导

首先,Paxos算法的必须要能满足第一个条件:

P1:一个Acceptor必须接受它收到的第一个议案。

要满足这个条件实在太过简单了,方法略。。。

下面是我个人对这个条件的理解,为什么必须满足这个条件:

假设只有一个Acceptor,只有一个Proposer。如果Acceptor出于某些原因拒绝了Proposer的议案,那必然导致Paxos的目标T1无法达成。因此可以认为目标T1隐含了P1。

在开始P2的推导的前,为了区分不同议案,需要先对每个Proposer的议案进行编号,编号时必须保证每个议案的编号具有唯一性(不讨论实现方法),而且编号是不断增大的。

Paxos的目标T2隐含了P2:

P2:如果一个值为v的议案被选定了,那么被选定的更大编号的议案,它的值必须也是v。

P2很容易理解,除了其中的一个形容词“更大编号的”,这个形容词很扎眼,为什么只对更大编号的议案进行限制,更小的编号怎么办?

老头子给的解释很简单“By induction on proposal number”(如果不看论文后半部分,没人知道他在说什么…)我说一下我自己的理解:

首先把“更大编号的”几个字换成“其他的”,我们称它为P2S。那么P2S能否满足Paxos的目标?答案是肯定的。然后比较P2和P2S,谁的约束更强?这得看“更小的编号”是怎么处理的,从论文后面的推演来看更小编号的议案绝对不允许被选定!!!因此满足P2的议案是P2S的一个子集。

显而易见,P2S和P2都能满足Paxos目标。换句话说,能满足Paxos目标的办法很多,但我们只选其中一个办法就OK了。不过,要选最简单的办法(看完后面就知道了)。

总之,现在我们可以得出一个结论:

如果P1和P2都能够被满足,那么Paxos的两个目标就能够达成。

如果你对上面这个结论没有异议,那么就说明你已经充分理解了P1和P2。

接下来就需要想办法,如何才能满足P2:议案在选定前,都要先被Acceptor接受,因此要满足P2,我们只要满足下面的条件:

P2a:如果一个值为v的议案被选定了,那么Acceptor接受的更大编号的议案,它的值必须也是v。

P2a是P2的充分条件,但是P2a存在一个大麻烦:当一个议案被选定后,一部分Acceptor无法立刻获得通知。例如下图中A1和A2已经接受了“赵”,这时“赵”就被选定了,此时Pb向A3提出了一个议案“钱”,这是A3接受的第一个议案,为了满足P1,A3必须接受这个议案,此时就会导致P2a无法被满足了。

为了解决上述的问题,我们想一下:要是此时不让Pb提出“钱”这个议案,而是提出“赵”这议案就万事大吉了。顺着这个思路,我们得到了P2b:

P2b:如果一个值为v的议案被选定了,那么Proposer提出的更大编号的议案,它的值必须也是v。

P2b是一个比P2a更强的约束,也就是说P2b是P2a的充分条件,只要能满足P2b,那P2a就自动满足。但P2b很难被满足,考虑下图这种情况,A1接受了议案“赵”,A2即将接受议案“赵”,此时Pb提出了一个议案“钱”,这种情况下我们又会遇到跟P2a完全相同的麻烦。

很明显,要想满足P2b,我们必需让Proposer拥有“预测未来”的能力,这听起来像在讲鬼故事,后面会想办法解决这一点。

在介绍如何“预测未来”之前,我们必须先确定Proposer在提出一个议案时,它的值该如何选取,因为取值的方法决定了“预测”的方法。

一个理所当然的取值方法:找到一个Acceptor的多数派集合,集合内被接受的议案的值都是v,此时Proposer提出一个新的议案,议案的值必须也是v;如果没有这样的多数派集合,那Proposer就任意提。

这个取值方法,完全能符合P2b,这是一目了然的,但问题出在 “预测”上,我们必须能预测到即将形成多数派的那个议案,如果有谁能做到那就真的是在讲鬼故事了。

Proposal提出议案的正确姿势:

P2c:在所有Acceptor中,任意选取半数以上的Acceptor集合,我们称这个集合为S。Proposal新提出的议案(简称Pnew)必须符合下面两个条件之一:

  1. 如果S中所有Acceptor都没有接受过议案的话,那么Pnew的编号保证唯一性和递增即可,Pnew的值可以是任意值。
  2. 如果S中有一个或多个Acceptor曾经接受过议案的话,要先找出其中编号最大的那个议案,假设它的编号为N,值为V。那么Pnew的编号必须大于N,Pnew的值必须等于V。

P2c提出议案的规则有点复杂,它真的能满足P2b吗?至少看上去不是那么一目了然…..老头子用了归纳法来证明P2c能满足P2b,但效果不佳,没什么人能看懂,所以下面的证明过程即使你看不懂也必要太沮丧(后面会给出图文解释)。

证明题(注意!前方高能):

已知议案

是集合中第一个被选定的议案,接受这个议案的Acceptor集合为

,在满足P2c的规则2的情况下,提出了一个新的议案

,n>m,证明

第一步,证明初始成立:当议案的编号n = m+1时,证明

因为

是第一个被选定的议案,因此在m+1提出之前,m必然是集群当中编号最大的议案。

根据P2c的规则2,议案

能够被提出,是因为存在一个多数派集合

,这个集合中,编号最大的议案的值为

。因为

都是多数派集合,所以他们必定存在交集。交集中的Acceptor必定都接受了

。m是整个集群最大的编号,当然也就是

中最大的编号,根据P2c的规则2,

必定等于

第二步,当n > m+1时,假设编号从m+1到n-1的议案的值都是

,证明

编号为m+1到n-1的议案提出后,我们没办法判断究竟那一个议案会被选定,但有一点是可以肯定的:所有接受了

的Acceptor构成了一个新的集合

,这个集合包含了集合

中的所有Acceptor,

显然是一个多数派集合,这个集合接受的议案的编号在m到n-1之间,而且值为

。没有包含在集合

中的Acceptor所接受的议案一定小于m。

根据P2c的规则2,议案

能够被提出,那么一定存在一个多数派集合

,因为

都是多数派集合,所以他们必定存在交集。交集中的议案的最大编号一定大于等于m,小于等于n-1。因此集合

中编号最大的议案一定位于交集内。根据P2c的规则,

必定等于

这个证明过程,如果你能看懂,请受我三跪。。。

接下来,上图,举例说明。

假设有一个议案(3,Va)提交后,这个议案成为了被Acceptor集群选定的第一个议案 ,那此时集群的状态可能会如下图所示:

一共5个Acceptor,有3个Acceptor接受了议案(3,Va),刚刚过半。此时有一个编号为4的议案要提出,根据P2c的规则2,首先选一个过半的集合,就选上图中蓝色线圈出来的A3,A4,A5好了(任意选),这个集合中编号最大的议案是(3,Va),因此新提出的议案必定为(4,Va)。符合P2b。

议案(4,Va)提出后,集群的状态可能是下面这样:

此时再提出编号为5或6,7,8,9,10的议案,这个议案的值必定也是Va(不信的话请举出反例),符合P2b。依此类推。。。

由此可证,P2c是能够满足P2b的!!!

想想看P2,P2a,P2b中为什么一定要有“更大编号的”这几个扎眼的字眼,此时你应该能有一点感觉了,可能你会把它理解成“后提出的”,如果你是这样理解的话,请往下看。

有些童鞋肯定早就已经想到了:当议案(3,Va)提交后,这个议案成为了被Acceptor集群选定的第一个议案,此时集群的状态有没有可能是下面这样?

注意,这时议案(4,Vb)才是集群当中的编号最大的议案,要是这样就糟糕了!当我们提出编号为5的议案时,它的取值就有可能是Vb,导致无法满足P2b。

为了保证不出现这种情况,就要用到前面提到的“预测未来”的能力。跟P2c的议案规则相配套的,需要预测的未来是:

当一个议案在提出时(即使已经在发送的半路上了),它必须能够知道当前已经提出的议案的最大编号。

这样的话,议案(3,Va)提交时,就会知道有一个(4,Vb)的议案已经提交了,然后将自己的编号改成5或更大编号提交,一切就完美了。

但是你知道的,我们并不可能真的预测未来,换个思路,议案肯定是要提交给Acceptor的,只要由Acceptor来保证议案编号的顺序就OK了。于是有:

议案(n,v)在提出前,必须将自己的编号通知给半数以上的Acceptor,收到通知的Acceptor将n跟自己之前收到的通知进行比较,如果n更大,就将n确认为最大编号,当半数以上Acceptor确认n为最大编号时,议案(n,v)才能正式提交。

两个编号不同的议案,不可能同时被确认为最大编号,证明略。

但是实际环境上,上面的条件还不足以保证议案被接受的顺序,比如议案(n,Va)被确认为最大编号后,开始向Acceptor发送,此时(n+1,Vb)提出,由于网络速度的原因,(n+1,Vb)可能比(n,Va)更早被Acceptor接收到。

因此Acceptor收到一个新的编号n,在确认n比自己之前收到的编号大时,必须做出承诺(Promise):不再接受比n小的议案。

这个承诺会导致部分漏网之鱼(在发送途中被抢走最大编号的议案),无法形成多数派。

例如下图所示:有一个在途的议案(1,Va),当A2和A3对议案(2,Vb)做出承诺的同时,(1,Va)就失去了形成多数派的权利。

至此,我们就形成了一个完整的算法(具体实现请自行搜索PhxPaxos)。

后记

算法原文中,将Promise看做是P2c的具体实现,而我们将Promise看成是弥补P2c的补充条件。这两者没有质的差别,只是角度不同,我个人认为后一种更容易被理解,所以采用了后一种。不过采用后一种会遇到下面的麻烦:

按下面的顺序提交议案:

  • 议案(1,Va)向A1发送Prepare,获得A1的承诺。
  • 议案(2,Vb)向A1发送Prepare,获得A1的承诺。
  • 发送议案(1,Va)

此时A1会拒绝议案(1,Va)

采用后一种解释的话,会发现A1拒绝议案(1,Va)是违反了P1的,而采用前一种解释则不违反P1。(这不过是个文字游戏,我已经懒的去思考了,就这样吧)

如果我们将半数以上的Acceptor对同一个议案(n,v)做出承诺的状态称作是“锁定”状态。那么这个“锁定”状态具有以下性质:

排它性:所有比n小的议案都不允许提交,已经在途的议案,则不允许其形成多数派。

唯一性:任意时刻,全局只有一个议案能获得“锁定”状态。

原子性:议案n从锁定状态变为非锁定状态的过程是原子的,议案n+1从非锁定状态变更为锁定状态的过程也是原子的。

我相信(有点虚…),正是上面的这三条性质保证了一致性。

最后,感谢老头子给出的如此精彩的算法。

本文参与 腾讯云自媒体同步曝光计划,分享自微信公众号。
原始发表:2018-12-25,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 JAVA高级架构 微信公众号,前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
暂无评论
推荐阅读
编辑精选文章
换一批
Redis 中的 3 种特殊数据类型
Redis 中除开最常用的 5 种数据类型之外,还有 3 种特殊的数据类型,他们是:
村雨遥
2020/09/11
7400
Redis 的 8 大数据类型,写得非常好!
最近这几天的面试每一场都问到了,但是感觉回答的并不好,还有很多需要梳理的知识点,这里通过几篇 Redis 笔记整个梳理一遍。关注公众号Java技术栈回复面试也可以刷我整理的系列面试题。
Java技术栈
2020/12/08
9880
Redis数据类型及命令
Redis数据类型及命令 基本数据类型 String - 字符串 我提前存入了两个数据 k:v 、z:c 追加字符串 追加字符串,如果当前可以不存在就相当于 set APPEND xxx value 获取字符串长度 STRLEN XXX 自动加 1 INCR XXX 自动减 1 DECR XXX 设置步长,指定增量 INCRBY XXX 数值 设置步长,指定减量 DECRBY XXX 数值 截取字符串 GETRANGE k 0 3 #截取字符串[0,3] GETRANGE k 0 -
张小驰出没
2021/12/06
5090
Redis数据类型及命令
Redis 数据类型
string是redis最基本的类型,一个key对应一个value string类型是二进制安全的,即它可以包含任何数据
全栈程序员站长
2022/07/25
3360
Redis 狂神说
后来,随着访问量的上升,几乎大部分使用 MySQL架构的网站在数据库上都开始出现了性能问题
Remember_Ray
2021/04/07
1.1K0
Redis 狂神说
Redis详解
redis会周期性的把更新的数据写入磁盘或者把修改操作写入追加的记录文件,并且在此基础上实现了 master-slave(主从)同步。 免费和开源!是当下最热门的 NoSQL 技术之一!也被人们称之为结构化数据库!
JokerDJ
2023/11/27
4180
Redis详解
【Redis基础】Redis新数据类型(Bitmaps,HyperLoglog,Geospatial)命令简介与案例演示
Bitmaps 并不是实际的数据类型,而是定义在String类型上的一个面向字节操作的集合。因为字符串是二进制安全的块,他们的最大长度是512M,最适合设置成2^32个不同字节。 bitmaps的位操作分成两类:1.固定时间的单个位操作,比如把String的某个位设置为1或者0,或者获取某个位上的值 2.对于一组位的操作,对给定的bit范围内,统计设定值为1的数目(比如人口统计)。 bitmaps最大的优势是在存储数据时可以极大的节省空间,比如在一个项目中采用自增长的id来标识用户,就可以仅用512M的内存来记录40亿用户的信息(比如用户是否希望收到新的通知,用1和0标识)
小尘要自信
2023/10/10
3110
Redis被问到哭?看完这篇,她站起来了……
本篇将介绍redis常用基础命令,可当手册使用;理论篇干货在昨天的福利中有pdf和xmind资源,详见网盘链接。如果觉得安装环境总是出现问题,可以使用在线平台测试Redis命令:http://try.redis.io/ 日拱一卒,我们开始吧!
才浅Coding攻略
2022/12/12
3470
Redis被问到哭?看完这篇,她站起来了……
Redis学习笔记(七)
在微博应用中,可以将一个用户所有的关注人存在一个集合中,将其所有粉丝存在一个集合。Redis还为集合提供了求交集、并集、差集等操作,可以非常方便的实现如共同关注、共同喜好、二度好友等功
用户1289394
2021/12/29
2880
redis(五大数据类型)「建议收藏」
现来聊一下什么是同步,异步,阻塞和非阻塞 同步和异步的概念描述的是用户线程与内核的交互方式:同步是指用户线程发起IO请求后需要等待或者轮询内核IO操作完成后才能继续执行;而异步是指用户线程发起IO请求后仍继续执行,当内核IO操作完成后会通知用户线程,或者调用用户线程注册的回调函数。
全栈程序员站长
2022/09/21
3300
Redis笔记(二):Redis数据类型
Redis支持五种数据类型:string(字符串),hash(哈希),list(列表),set(集合)及zset(sorted set:有序集合)。
朝雨忆轻尘
2019/06/19
6330
新手村:最适合新手的 Redis 基础
简而言之,Redis是一个开源的使用ANSI C语言编写、遵守BSD协议、支持网络、可基于内存亦可持久化的日志型、Key-Value数据库,并提供多种数据类型,功能挺强。
syy
2020/04/07
5610
redis管理
Redis发布消息通常有两种模式: • 队列模式(queuing) • 发布-订阅模式(publish-subscribe) 任务队列:顾名思义,就是“传递消息的队列”。与任务队列进行交互的实体有两类,一类是生产者(producer),另一类则是消费者(consumer)。生产者将需要处理的任务放入任务队列中,而消费者则不断地从任务独立中读入任务信息并执行。 任务队列的好处: • 松耦合。 生产者和消费者只需按照约定的任务描述格式,进行编写代码。 • 易于扩展。 多消费者模式下,消费者可以分布在多个不同的服务器中,由此降低单台服务器的负载。
张琳兮
2018/09/10
7190
redis管理
Redis三种特殊类型
朋友的定位, 附近的人, 打车的距离计算 Redis的Geo在Redis3.2版本就推出了.这个功能可以推算地理位置信息, 两地之间的距离, 方圆几里的人 首先需要一个城市经度纬度查询工具 自行百度搜索
后端码匠
2021/01/20
6570
只需5分钟,完成Redis所有命令操作~
Redis存储的是key-value结构的数据,其中key是字符串类型,value数据类型有:
田维常
2023/11/14
4740
只需5分钟,完成Redis所有命令操作~
【Redis】005-Redis三大特殊数据类型:geospatial地理位置、HyperLogLog基数统计、Bitmaps位图
将指定的地理空间位置(纬度、经度、名称)添加到指定的key中。这些数据将会存储到sorted set这样的目的是为了方便使用GEORADIUS或者GEORADIUSBYMEMBER命令对数据进行半径查询等操作;
訾博ZiBo
2025/01/06
1220
【Redis】005-Redis三大特殊数据类型:geospatial地理位置、HyperLogLog基数统计、Bitmaps位图
Redis00--Redis的基本命令
Redis hash 是一个键值对集合,Redis hash是一个String类型的field和value的映射表,hash特别适合存储对象,例如SpringSession中的session信息,存储用户信息,用户主页访问量等等。
码农飞哥
2021/08/18
3610
Redis 详解
redis是一个开源的、使用C语言编写的、支持网络交互的、可基于内存也可持久化的Key-Value数据库。
云计算小黑
2022/12/28
4220
Redis 详解
redis 乐观锁_数据库乐观锁实现
存储地理位置的数据结构 应用场景 朋友的定位,附近的人,打车距离计算 Geospatial底层使用的是Zset
全栈程序员站长
2022/09/22
5990
Redis 数据类型总结
Redis是一个开源的,基于内存的数据结构存储系统,它可以用作数据库、缓存和消息中间件。
windealli
2024/05/20
2440
Redis 数据类型总结
相关推荐
Redis 中的 3 种特殊数据类型
更多 >
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档