当前位置: 首页 > 工具软件 > Pax > 使用案例 >

关于Paxos(一)

陆城
2023-12-01

本文来自知行学社视频:https://www.bilibili.com/video/BV1Lt411m7cW/

问题

一段数据更新序列:[op1, op2, … , opi],要在所有节点中对这个序列建立共识,确定第i个操作opi是什么。

这里将var比作opi,proposer提交var即提交操作。

  • 角色:
    • Acceptor:存储管理var
    • Proposer:并发调用API,提交不同的var
  • API:propose(var,V) ----> <ok, f> or <error>
  • 问题:保证var的一致性,如何管理Proposer的并发执行?

方案一,互斥锁:

​ 通过互斥锁:

​ P向A请求到互斥访问权限,A才可以接收请求。

​ 存在的问题:P在释放锁之前出现故障,会导致整个系统死锁。

方案二,抢占式访问权:

​ 引入抢占式访问权:

1. A可让某个P的访问权失效,再重新发放
2. P向A申请访问权时,需要指定epoch(越大越新)
3. A采用喜新厌旧的原则:
 * 一旦接受大的epoch,马上让旧的访问权失效,不再接收他们的请求
 * 给新的epoch发放访问权,只接受新epoch的请求
4. 新epoch可以抢占旧epoch,让旧的epoch访问失效
5. 为了保持一致性,不同epoch的proposer之间采用**后者认同前者**的原则:
* 在肯定旧的epoch无法生成确定性取值时,新的epoch会提交自己的value。不会冲突。
* 一旦旧的epoch生成确定性取值,新的epoch一定会认同此取值,不会破坏。

Acceptor的实现:

  • Acceptor保存的状态:

    • 当前var的取值<acceptor_epoch, acceptor_value>
    • 最新访问权的epoch:latest_prepared_epoch
  • Acceptor::prepare(epoch):

    ​ 只接受比latest_prepared_epoch更新的epoch,给予访问权,返回var的值

  • Acceptor::accept(var, prepared_epoch, v):

    • 先验证prepared_epoch是不是最新的epoch
    • 更新var的取值为:<prepared_epoch, v>

Proposer的实现

propose(var, V)的两阶段实现:

  • 第一阶段,获取epoch轮次的访问权和当前var的取值:

    • 以当前时间戳为epoch,调用Acceptor::prepare(epoch)获取访问权限和当前var的值
    • 不能获取则返回<error>
  • 第二阶段,采用后者认同前者的方案执行

    • 若获取的var为空,说明旧的epoch没有生成确定性取值,可以提交自己的数据Acceptor::accept(var, prepared_epoch, v),成功则返回<ok, v>;失败返回<error>,说明访问权被更新的epoch占用(也有可能时acceptor故障)
    • 若var取值存在,说明是确定性取值,认同它不再更改,返回<ok, accepted_value>

运行过程:

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-eCzdpzJI-1638085316971)(新建 Markdown.assets/image-20211128143002603.png)]

总结

可以避免死锁问题,并保证var的一致性,但是存在acceptor的单点故障问题

Paxos

  • 在方案2的基础上,引入多个acceptor:

  • Acceptor实现不变。

  • Acceptor采用少数服从多数思路

  • 一旦某个epoch的取值f被半数以上acceptor接收,则该值被确定,不会被更改

Proposer的实现

  • propose(var, V)第一阶段不变:

    • 获取半数以上acceptor的访问权和对应一组var的取值
  • propose(var, V)第二阶段,采用后者认同前者原则:

    • 获取的var都为空,说明旧epoch无法生成确定性取值。努力使<epoch, V>成为确定性取值:

      • 向epoch对应的acceptor发送提交取值<epoch, V>
      • 收到半数以上成功,返回<ok, V>
      • 否则,返回<error>
    • 如果var存在取值f,努力使<epoch, f>成为确定性取值:

      • f出现半数以上,已经是确定性取值,可以直接返回<ok, f>
      • 否则,向epoch对应的所有acceptor提交取值<epoch, f>

    假设一种情况:epoch1的取值f是确定性取值(100个acceptor里面,51个接收该值),epoch2获取的过半数acceptor是49个未接收epoch1的acceptor+2个接收的acceptor,epoch2会收到来自那2个acceptor的返回值f,因为后者认同前者,epoch2会认同f,不会更改,并向所有acceptor提交f。

    为什么acceptor返回的值不统一?因为epoch1刚刚写入51个acceptor,就被epoch2占用了访问权

 类似资料: