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。上述过程失败,则定期重试。
相关文章推荐
- jquery笔记(基础知识)
- IDEA Error assembling WAR: webxml attribute is required (or pre-existing WEB-INF/web.xml if executin
- uva 122
- 加密解密基础及私有CA的实现-2015092801
- 新的征途
- Leetcode 7 Reverse Integer
- hashtable ,hashmap的containsValue与containsKey
- 我的人生感想
- poj 3074 Sudoku(Dancing Links)
- 编写数据库常用的增、删、改、查代码(1)。
- Android桌面组件App Widget用法入门教程
- 查看SDImageCache缓存图片地址(暂时仅知道模拟器上的地址)
- 随便写写
- MySql修改表结构
- 连接数据库工具类调用程序
- HDU1164_Eddy's research I【Miller Rabin素数测试】【Pollar Rho整数分解】
- 万能适配器
- Android Studio在SVN上创建分支
- 移动构造-C++11
- Jetty9.3 翻译