您的位置:首页 > 其它

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

2015-09-28 12:23 316 查看

引子

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

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

基本背景和名词解释:

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

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

实现方案:

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



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

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

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

paxos实例

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

primary给secondary同步数据

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

冻结与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。

每个replica需要记录的值

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

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

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

每个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状态,并将此文件移动到冻结目录。

冻结的触发以及执行

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。上述过程失败,则定期重试。

内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: