Paxos算法

admin 2289

解决什么问题

paxos用于确定一个不可变变量的取值。这看似是一个简单的功能,但是分布式系统中时刻伴随着乱序,故障的现象,并且为了提高系统可用率,存储采用多个副本存储,此时确定一个变量的取值就不是一个简单的功能了。

应用

以分布式式存储系统为例,数据的值是可以一直变化的,但是其存储方式可以拆分为不同的变更记录,使用paxos来确定每一次变更的确定性取值。

问题分析:

paxos解决的问题系统抽象:确定一个变量var的取值

系统组成:

系统内部由多个acceptor组成,负责存储和管理var变量。

系统外部由多个proposer组成,负责发起proposal(n,v),设置变量var的值,其中编号是n,值是v

系统主要要求:

var的取值的一致性,如果var取值没有确定,则系统认为var取值为NULL,一旦系统认为var取值被确定了,var的值不可被更改

额外要求:

多个proposer可能并发调用,并且可以提出不同的取值。

系统能容忍少数(半数以下)accetpor故障。

系统能容忍任意proposer在任何时候发生故障。

当前假设:acceptor不会丢失数据。

统一术语:

val=v被选中 => v被写入超过超半数的acceptor

paxos算法:

acceptor需要存储的元素:

prepare最大num,记为PreNum

已接受的proposal(n, v)中,编号n最大proposal,记为accept(ProNum_ProVal),没有接受过proposal则记为accept(null)

阶段1:准备阶段

a(proposer做法):向超过半数的accetpor发送一个num=n的预请求,记为prepare(n)

b(acceptor做法):acceptor收到prepare(n)后,对比PreNum和n

如果PreNum>n,回复reject()

否则,acceptor接受该prepare(n),并设置PreNum=n,并返回accept(ProNum_ProVal)或者accept(null)

阶段2:提交阶段

a(proposer做法):根据阶段1a中的返回值。

如果收到至少一个reject(),则返回

如果收到的回应数没超过半数,则返回

如果收到超半数不为reject()的回应,根据阶段1b中的返回值

如果所有返回值为accept(null),则proposer向所有acceptor发送acceptI(n, v)。

取返回值accept(ProNum_ProVal),判断v的个数是否超过半数

是 => v被选中,返回

否 => 向所有accetpor发送proposal(n, v),其中v是所有accept(ProNum_ProVal)中ProNum最大对应的ProVal,n是节点1a中的num。

b(acceptor做法):根据节点2a接收到的proposal(n, v) , 判断PreNum和n

n

否则,接收proposal(n, v),记录accept(n_v),并广播accept(n_v)

论文的推导过程理解:

多个acceptor存储val,如何才被定义val被选中?

超过半数acceptor写入成功某个取值v时,称val被选中 => 理论是抽屉原理,两个超半数的acceptor集合必有一个acceptor重合,最先写入超半数acceptor集合成功的val(v)会被选中,后续的超半数集合acceptor必然能获取到该选中的val(v)。

为什么要添加自增编号num?

需要写入超半数acceptor && 容忍半数以下的acceptor故障。acceptor总个数为5,v1、v2写入成功acceptor的数量=2,此时剩下的一个acceptor故障 => 其中一个acceptor必然会写入两次val,写入是通过添加自增编号(规定高阶=>num更大)区分两次,甚至多次写入。

既然一个acceptor会写入多个值,如何确保val的取值不可变?(被选中之后,不在提出写入其他值)

一旦proposer发现val=n的值被选中,则使用已经被选中的val写入acceptor。

更加严苛的做法(找其充分条件,论文中的证明过程就是一步步找满足不可变条件的充分条件,直至工程上容易实现)

假设当num=m时,val=v已经写入超过半数acceptor,有num=n(n>m),proposer写入任意一个acceptor的val都应该是v => 简称P2a

假设当num=m时,val=v已经写入超过半数acceptor,有num=n(n>m),proposer都只能发送proposal(n,v) => 简称P2b

如何确保P2b? => P2c(最难理解的部分)

在发出proposal(n,v)之前

要么确保超半数acceptor没有接受过任何自增编号小于n的proposal(证明还没有任何va被选中,此时可以v可以是任何值)。

要么确保超半数acceptor所接受的所有proposal中,最大小于n自增编号的proposal对应的val为v (归纳法可证明,如果v已经被超半数acceptor所接受,每次取最大num对应的v,那高阶的proposal的val一定是v)

两阶段提交中的阶段1 prepare解决了什么问题?

查询是否有val已经被选中

由于proposer的调用会乱序,通过自增编号num=n锁定acceptor,使得acceptor不在接受num

算法每次取acceptor所接受的proposal中编号最大的v来广播,保证了2件事情

在没有选中前快速收敛

选中后取值一致

算法保证了当val=v的值被写入超过半数后,高阶的num对应的val一定是v,所以以下情况不会出:

accept1

accept2

accept3

PreNum

-

-

-

accept(ProNum_ProVal)

accept(1_1)

accept(1_1)

accept(2_2)