Paxos算法 (土豆视频)

Paxos用来确定一个不可变变量的取值。不可变变量的取值一旦被确定,将不再被更改,并且可以被获取到。

确定一个不可变变量的取值和分布式存储系统的关系:
在存储系统里面,数据本身是可变的,用户可以进行任意的增删改查,分布式存储系统为了保证数据的可靠存储一般会采用多副本的方式进行存储。对多个副本的更新操作序列[Opi1, Opi2, Opi3,….]是相同的,不变的。
用paxos算法依次来确定不可变变量Opi的取值,即第i个操作是什么。每确定完Opi以后,可以让各个数据副本去执行Opi,然后以此类推,不断地更新下去。

确定一个不可变变量的取值 <=> 设计一个系统,来存储名称为var的变量。
  • 系统内部由多个Acceptor组成,负责存储和管理var变量
  • 外部有多个Proposer机器任意并发调用API,向系统提交不同var取值
  • 系统需要保证var的一致性:
    • 如果var的取值没有确定,则var的取值为null
    • 一旦var的取值被确定,则不可被更改。并且一直可以获取到这个值
  • 系统需要满足容错特性:
    • 可以容忍任意Proposer机器出现故障
    • 可以容忍少数Acceptor故障(半数以下)

方案一
整个系统由单个Acceptor组成,通过类似互斥锁机制,来管理并发执行的Proposer运行。
Proposer首先向Acceptor申请Acceptor的互斥访问权,然后才能请求Acceptor接受自己的取值。
Acceptor给Proposer发放互斥访问权,谁申请到互斥访问权,就接收谁提交的取值。

让Proposer按照互斥访问权的顺序依此访问Acceptor。
一旦Acceptor接收了某个Proposer的取值,则认为var取值被确定,其他Proposer不再更改。

第一阶段:Proposer通过prepare获取互斥访问权和当前的var的取值。如果不能获取到,则说明互斥性访问权已经被其他Proposer获取到,并且还没有释放,所以无法进入第二阶段运行。
第二阶段:根据当前var的取值f,选择执行:
  • 如果f为空,则说明历史上没有任何Proposer将var的取值设置成功,则此时提交自己的数据v
  • 如果f不为空,则说明历史上某一个Proposer已经将var的取值设置成功。为了满足一致性要求,此Proposer不可以更改var的取值,所以在这种情况下Proposer会释放掉访问权。

Proposer在释放互斥访问权之前发生故障,会导致系统陷入死锁。

方案二
在方案二里面Acceptor可以让某个Proposer获取到的访问权失效,不再接收它的访问。之后,可以将访问权发送给其他Proposer,让其他Proposer访问Acceptor。这样,解决了方案一中的死锁问题。

Proposer向Acceptor申请访问权时指定编号epoch(越大的epoch越新),获取到访问权之后,才能向Acceptor提交取值。
Acceptor采用喜新厌旧的原则:
  • 一旦收到更大的新epoch申请,马上让旧epoch的访问权失效,不再接收它们提交的取值。
  • 然后给新epcoh发放访问权,,只接收新epoch提交的申请。

新epoch可以抢占旧的epoch,让旧epoch的访问权失效。旧epoch的Proposer将无法运行,新epoch的Proposer将开始运行。

为了保持一致性,不同epoch的Proposer之间采用“后者认同前者”的原则:
  • 在肯定旧epoch无法生成确定性取值时,新的epoch会提交自己的value,不会冲突。
  • 一旦旧epoch形成确定性取值,新的epoch肯定可以获取到此取值,并且会认同此取值,不会破坏。
如果var的值为空,则肯定旧epoch无法生成确定性取值,Proposer提交自己的数据v。如果失败,说明Acceptor被更新的Proposer抢占到访问权,那么不再接收当前Proposer提交的请求。
如果var取值存在,那我们知道历史上已经有一个Proposer在某个epoch里面获取到访问权以后将var的取值设置成功。则此取值肯定是确定取值,此时认同它不再更改。

p1向Acceptor发送prepare(#epoch1)的请求申请epoch1的访问权,Acceptor向p1返回当前系统var的取值=null,p1第一阶段的运行已经结束,它拿到了epoch1的访问权。在运行第二阶段之前,p2开始抢占epcoh2的访问权,因为对Acceptor来说epoch2比epoch1大,所以它首先让epoch1的访问权失效掉,然后让epoch2访问权生效,向p2返回当前Acceptor接收的var的取值null。因为p2已经获取到了epoch2的访问权,所以开始运行第二阶段,向Acceptor提交自己的取值v2。Acceptor发现epoch2是自己当前发放访问权最大的epoch,所以接收这个请求,将var的取值设置成(#epoch2, v2),并向p2返回结果。然后在这个过程中p1的第二阶段延迟运行,然后到来,向Acceptor发出accept(#epoch1, v1)的请求,因为访问权已经被p2所抢占,epoch1已经失效掉了,所以Acceptor不再接收这个请求。p3向Acceptor发出prepare(#epoch3)希望获取epoch3的访问权,因为当前Acceptor发放的访问权最大的epoch是epoch2,所以它让epoch2失效掉,然后让epoch3生效,向p3返回Acceptor已经设置成功的取值(#epoch2, v2)。对于Proposer3来说,它发现系统内部已经生成了确定性取值(#epoch2, v2),此时不进行更改。

方案三
仍需要引入多Acceptor
  • 单机模块Acceptor导致整个系统宕机,无法提供服务

Paxos采用少数服从多数的思路
  • 一旦某epoch的取值f被半数以上acceptor接受,则认为此var取值被确定为f,不再更改

第一阶段:获取半数以上Acceptor的访问权和对应的一组var取值
第二阶段:采用“后者认同前者”的原则执行

Proposer1在第一阶段选定了epoch1,希望获取到epoch1的访问权。首先p1会向3个Acceptor发出prepare(#epoch1)请求。3个Acceptor直接对epoch1发放访问权,并且返回当前var的取值全部为null。epoch1获取到半数以上Acceptor访问权后进入第二阶段。首先,epoch1发现全部的var取值都是null,所在在这种情况下epoch1可以选定Proposer1自己的取值v1,努力使v1成为确定性取值。所以epoch1开始向Acceptor发出accept请求。首先epoch1向第一个Acceptor发出了请求,希望它接收epoch1选定的取值v1,当前第一个Acceptor发放访问权的epoch就是epoch1,所以直接选定它的取值然后返回成功。在epoch1向另外两个Acceptor发出请求之前,Proposer2在第一阶段选定了epoch2,希望抢占epoch2的访问权,所以开始向前两个Acceptor发出prepare请求。对于前两个Acceptor来说,当前发放访问权的就是epoch1,因为epoch1小于epoch2,所以直接让epoch1的访问权失效,给epoch2发放访问权,并且返回当前var的取值,第一个取值是epoch1提交的取值v1,第二个取值是null。然后进入第二阶段运行。在epoch2的第二阶段运行之前,epoch1开始向另外2个Acceptor发出accept请求,希望它们接收epoch1选定的取值v1,因为第二个Acceptor的访问权已经被epoch2抢占,epoch1已经失效掉了,所以不再接收epoch1的请求。第三个Acceptor直接接收了epoch1的请求,设置(#epoch1, v1)然后返回成功。所以在这种情况下epoch1已经形成了确定性取值,也就是Proposer1自身的取值v1。然后epoch2的第二阶段开始运行。首先,选定取值里面最大的epoch也就是epoch1提交的取值v1(var的值存在,认同最大epoch即epoch1对应的取值v1,并努力使(#epoch2, v1)成为确定性取值),然后开始让Acceptor接收v1。对前两个Acceptor来说,发放访问权最大的就是epoch2,所以直接接收epoch2提交的取值,也就是(#epoch2, v1)然后返回成功。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值