GFS为什么要三个副本全写成功才算成功?——基于paxos实现多数派逻辑的方案设计

引子 

 GFS的三副本要求数据在三个副本都写成功,才会更新元数据,标志数据写入成功,为何不使用多数派机制呢?

 这是因为,写多少乍一想非常简单,但是要设计一个正确的、可容错的、严格一致性的多数派写入方案还是非常复杂的,本文给出一个可选的写入方案。

基本背景和名词解释:

1.基本架构和GFS一致,数据流被切分为多个record,写入数据分片,数据写三副本,并存在元数据服务器,数据分片称之为Partition,主称之为primary,备称之为secondary,主写满64MB后会冻结当前Partition,再新创建一个Partition,备发现主lease超时后,即便当前Partition还未写满64MB,也会主动发起冻结。

2.要求是,冻结后的多个副本是一致的,即每个副本record的数量和内容都是一致的


实现方案:

冻结可能在两种情况下触发:

  • primary写满(比如64MB)之后,主动发起冻结

  • secondary发现primary lease过期,并且元数据也没有处于冻结状态,也会发起冻结,并补齐多数派上的数据

为了在这一分布式事件中保持数据的一致性和完备性,并说明其正确性,还需引入paxos算法,下面对这一过程和paxos的对应关系和代码简要设计说明:

  1. paxos实例

在写入数据到三副本以及冻结的过程中,每个record就是一个实例,每个实例之间是没有关系的,实例的集合是recondid属于[0, 无穷大)


  1. primary给secondary同步数据

在这个过程中只有一个proposer,即primary,其被创建时就指定了身份,相当于   primay已经给所有副本以无穷小的一个proposalID发送了prepare请求,后续的record同步都是以这个无穷小的proposalID发送accept请求

  1. 冻结与multi-paxos

冻结的含义,是把[0, 无穷大)集合中所有实例都“确定”下来,其中要分为两个集合,(0, MaxRecordID] 这个集合的实例的值已经确定,(MaxRecordID, 无穷大)这个集合的值全部确定为NULL,注意,空值也是一个确定的值。

其中,primary写满一个partition之后,发送“tail”标志给secondary,相当于以无穷小的proposalID发送(MaxRecordID, 无穷大)区间的所有paxos实例的ACCEPT请求,这些实例的值都为NULL。

secondary发起冻结,相当于这个paxos group中有了新的proposer,需要用新的proposalID发送(StartRecordID, 无穷大)范围的prepare请求,其中,StartRecordID即为本地最大RecordID。根据paxos的约束,所有副本作为acceptor,需要返回本地已经存在的accept记录(即为本地的ID大于StartRecordID的record),收集到多数派的回应后,此proposal已经拥有全量数据,并且可以确定出全局的MaxID,之后可以随同accept请求把这些信息发给多数派。

全局MaxRecordID的值并不是由专门的paxos实例来决定的,而是在确定了(0, 无穷大)这个集合内所有Record的值之后,基于这些paxos实例的结果,推倒出来的。推倒的方法就是找到最大的非NULL值得RecordID。

  1. 每个replica需要记录的值

  • PPid:已经应答过的最大的Prepare的请求所携带的proposalID,初始化为0。

  • (Apid, maxID):已经应答过的最大的Accept请求的proposalID, 以及此请求所携带的最大得非NULL的RecordID,初始化均为0

  • state: 如果冻结成功则将其设置为FROZEN状态,并将此文件移动到冻结目录


  1. 每个replica对外暴露的接口以及相关实现

  • send_record()

primary给secondary(包括自己)同步record,如果PPid不为0,则拒绝;


  • prepare_record(pid,data_range)

如果pid<APid,则拒绝;否则返回返回本地已经位于data_range区间的record,并使得PPid =pid


  • accept_record(pid,maxID,Record_data)

如果pid<PPid,则拒绝,否则将MaxID记录在本地(标记(maxid,无穷大)已经为NULL值,如果有需要还需要将Record_data(其他节点已有而本地没有的Record)写入本地,并使得APid=pid

  • set_frozen(maxID)

某个replica执行frozen成功后,则发送此消息广播给所有replica,收到此消息后,修补或者裁剪本地record数据,修改stat状态,并将此文件移动到冻结目录。

  1. 冻结的触发以及执行

primary写满之后,直接发送accept_record(0, maxID, NULL)给secondary,收集到多数派回应后,更新元数据,并发送set_frozen()给所有replica;如果primary执行此步骤失败,则放弃自己primary的身份,之后处理逻辑与secondary相同。

secondary发现primary lease过期,并且元数据还长时间处于未冻结状态,则取当前时间戳加ip作为pid发送prepare请求给所有replica,如果收集到多数派的回应则发送accept请求给有过回应的replica,并携带此replica缺失的record.成功之后,需要更新元数据表,将此Partition设置为冻结状态,并广播此消息给所有repliaca。上述过程失败,则定期重试。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值