PacificA:微软设计的分布式存储框架

PacificA:微软设计的分布式存储框架

说明

随着信息量的急剧增长,大规模的分布式存储系统变得越来越重要。这些系统往往采用廉价的商用机器或硬件,失效出错成为了分布式存储系统的常态,因此,容错是这类系统实现可用性和可靠性的关键。众所周知已然被证明是正确的的复制协议“大有人在”,但是理论距离现实还是有一定差距的,理论可以不管不顾消息在网络中传递的次数,系统性能,吞吐量,系统设计的难易问题等等,但是真实的系统设计必须以整体的性能为根本依据,因此如何权衡理论和现实系统,设计一个可用,正确,高效的复制协议变得尤为重要。这个难题同样摆在了微软的面前,相比于单纯为了需求而设计一个具体的,特定的复制协议算法,微软高明地选择设计一个简单,实用,具有普适适用性的复制框架,这样不同的策略都可借助于该框架实现,并且有助于不同的策略优劣的比较。

PacificA是微软为大规模分布式存储系统开发的一个通用复制框架,也可称之为一个原型系统。该框架简单,实用,提供强一致性,并且可以适配不同的复制策略。它对于我们理解分布式系统的强一致性,容错,仲裁有很好的指导意义。PacificA的设计特点

  • 设计了一个通用和抽象的复制框架,模型容易验证正确性,实现不同的策略实例
  • 配置管理和数据复制分离,Paxos负责管理配置,主副本策略负责数据复制
  • 将错误检测和配置更新容在数据复制的交互里面,去中心化,降低瓶颈


系统框架

整个系统框架主要有两部分组成:存储集群配置管理集群

存储集群:负责系统数据的读取和更新,通过使用多副本方式保证数据可靠性和可用性;
配置管理集群:维护副本信息,包括副本的参与节点,主副本节点,当前副本的版本等。

数据被存储在数据分片上,数据分片实际上是数据集合,每个数据分片的大小基本固定,在存储节点上,数据分片表现为磁盘上的一个大文件。而数据分片也是多副本的基本单位。

配置管理集群维护的其实是分片的副本信息(分片位于哪些节点)。数据分片的副本称之为复制组(Replication Group)。因为每个存储节点上会存在众多的数据分片,因此,每个存储节点会位于多个复制组中。

配置管理集群维护所有分片的位置信息,配置管理集群使用Paxos协议维护该数据的一致性。存储集群以配置管理集群维护的分片位置信息为准。

数据复制框架

pacificA副本数据使用主从式框架来保证数据一致性。分片的多个副本中存在一个主副本Primary和多个从副本Secondary。所有的数据更新和读取都进入主副本,当主副本出现故障无法访问时系统会从其他从副本中选择合适的节点作为新的主。

在pacificA协议中,每个节点都维护

数据更新的流程如下:

  1. 更新记录进入主副本节点处理,为该记录分配Sn(Serial Number),然后将该记录插入prepare list,该list上的记录按照sn有序排列;
  2. 主副本节点将携带sn的记录发往从节点,从节点同样将该记录插入到prepare list;
  3. 一旦主节点收到所有从节点的响应,确定该记录已经被正确写入所有的从节点,那就将commit list向前移动,并将这些消息应用到主节点的状态机;
  4. 主节点提交后即可给客户端返回响应,同时向所有从节点发送消息,告诉从节点可以提交刚刚写入的记录了。


通过上面的分析可以知道,commit list和prepare list必然满足以下条件:

所有的读需要全部发往主节点,这是因为客户端来读时,主节点有可能尚未将commit消息发送至从,因此,如果读从节点可能会无法获取最新数据。当然,如果应用能够忍受一定的窗口,那读从也无所谓。

配置管理服务

前面的pacificA架构中我们提到该框架一个很重要的组成部分是配置管理服务。该服务维护所有复制组信息,主要是复制组的当前节点信息:

其中replica1,replica2…是复制组所有参与节点的地址,而version是当前复制组拓扑的版本,用来检测拓扑是否最新。

在系统运行过程中可能有以下几种情形导致复制组信息发生变更:

  • 复制组从节点离线;
  • 复制组主节点离线;
  • 复制组增加新节点


对每种情况处理方法:

  • 从节点离线,主节点能检测到该事件(检测方法下面部分说明),此时主节点向配置管理服务汇报最新的拓扑,注意此时的拓扑中已经不包含离线的从节点;
  • 主节点离线,从节点能检测到该事件(检测方法下面部分说明),此时从节点向配置管理服务汇报最新的拓扑,注意此时拓扑中已经不包含离线的主节点,同时从节点会将自己设置为主节点
  • 复制组增加新节点,可能是因为原来离线的节点又重新上线了,此时主节点向配置管理服务汇报最新的拓扑,注意此时的拓扑中加上该新增节点


需要注意的是:

节点在向配置管理服务汇报新的拓扑时,会将自己当前维护的拓扑信息的版本号+1,然后再提交给配置管理服务。而配置管理服务在收到拓扑更新请求时,会对比客户端传入的拓扑版本和自己维护的拓扑的版本,如果客户端的版本大于本地维护版本,接收之,否则,拒绝。

节点异常检测

系统运行过程中难免出现节点宕机离线等诸多异常。如何保证在节点异常情况下协议的正常运行是分布式系统设计中的关键问题。pacificA是一种强一致协议,通过主节点来维护多副本数据一致性。因此,需要确保每个副本的唯一主,避免二主问题的发生就非常关键。

pacificA使用了lease机制来保证不会产生二主问题。主节点与从节点维护心跳信息:主节点定期发送心跳,只要从节点响应该心跳,主节点就确定自己还是主。对于以下两种可能的异常:

  • 如果主节点在一定时间内(lease period)未收到从节点对心跳的回应,那主节点认为从节点异常,于是向配置管理服务汇报更新复制集拓扑,将该异常从节点从复制集中移除,同时,它也将自己降级不再作为主;
  • 如果从节点在一定时间内(grace period)未收到主节点的心跳信息,那么其认为主节点异常,于是向配置管理服务汇报回信复制集拓扑,将主节点从复制集中移除,同时将自己提升为新的主。


分析可知,只要lease period <= grace peroid,就不会产生二主问题,因为:

  • 假如主从之间出现网络分区。只要lease period <= grace peroid,主必然先检测到从没有回复的事件,于是他开始停止作为主服务,并且通知配置管理服务;
  • 由于lease period <= grace peroid,那么从节点检测到主节点异常则是在主节点停止作为主以后,此时该副本集是不存在主的,因此,必然不会产生二主现象。


说明:

  • 之前有个问题没太想明白:为什么主在检测到从节点异常时需要停止自己作为主的身份。

    如果不这样有可能会出现多主情况。分析一下,主节点不停止自己作为主,继续对外提供更新服务,而直接向配置管理服务汇报从复制集中移除从节点的消息M1,而从节点经过一段时间后向配置管理服务汇报移除主节点并将自己提升为新主的消息M2,但是由于网络传输的不确定性,配置管理服务可能会先处理M2,于是问题来了,这时候就产生了两个主,破坏系统的强一致性。

使用租约机制保证一致性是众多分布式系统的标准做法,例如GFS、Bigtable等。但是诸如GFS、Bigtable都是从一个集中式服务中申请lease,而pacificA则是一种去中心化式的租约机制。

另外,主从之间的数据流动也天然发挥了心跳信息的功能,因此,只要主从之间有数据交换,就可以无需发送心跳。

节点异常处理

从节点异常

从节点异常时,主节点降级,不再作为主,并向配置管理服务发送请求从复制集集中移除异常从节点。接下来,配置管理服务器更新本地的复制集,新的复制集中不再包含异常从节点。接下来从节点再向配置管理服务申请成为新的主,于是,接下来选出的新的主再对外提供服务。

主节点变更

当从节点检测到主节点异常时,向配置管理服务申请将主节点从复制集中移除并且将自己提升为新的主。一旦配置管理服务批准了从节点的请求,那从节点变成新的主,这时候主也被从副本集中剔除。

上面是Primary和Secondary-2两个节点之间出现了网络异常。接下来,配置管理服务器可能会收到三个节点发起的拓扑重配置请求M1,M2和M3。

M1:主节点DATA NODE 2将自己降级为从,同时向配置管理服务发起移除dn3的请求,我的理解是,如果配置管理服务批准了该请求,那么该节点重新成为主;
M2:从节点DATA NODE 3可能只是与主节点出现网络分区,进程还在,此时从节点DATA NODE 3向配置管理服务发起Change Primary请求并将主节点DATA NODE 2从复制集中移除;
M3:因为主节点DATA NODE 2放弃了主节点的身份,因此,可能导致DATA NODE 1在心跳周期内也无法获得心跳,于是它也有可能发起一次Change Primary请求,将主节点dn2移除,将自己提升为新的主。

而配置管理服务器会根据请求的先后顺序来处理,对于上述M1、M2、M3请求,只有一个会得到批准,假如M1得到批准,那么新的结构就变为:

此时DATA NODE 3便被从复制集中剔除了。当然,如果DATA NODE 3进程依然存活的话,它接下来还是可以向配置管理服务申请将自己加入复制组的。

在确定新的复制组拓扑后,还要经历一个reconciliation过程。该过程的主要目的是保持从节点和主节点的数据一致性,主要过程是主节点将自己的prepare list上的操作记录全部提交至状态机并将自己的prepare list记录同步至从。对于从节点来说,有可能其prepare list落后于主节点,这时候需要补齐,有可能其prepare list领先于从节点,这时候需要截断。无论如何,由于满足prepare list和commit list的定式关系,因此不会出现已经提交的记录被回滚的情况。

基于日志的数据同步方法

在讨论完pacificA框架运行机制后,论文的第三部分描述了基于日志在主从之间同步状态的几种方法,这里简单描述下,做法都是比较常见。

基于日志的状态机同步方案的架构一般如下:

LOG:日志,保证数据可靠性,所有的更新都是按照sn顺序写入日志,
IN-MEMORY: 内存状态机,当更新记录追加日志成功后便可以被更新在内存状态机;
CHECKPOINT:内存状态机容量有限,需要定期将内存状态机内容写入磁盘,内存状态机在磁盘的映像就是Checkpoint了,一旦执行了Checkpoint,那LOG中与该Checkpoint相关的日志项便可以删除了;
ON-DISK-IMAGE:CHECKPOINT数量众多,需要将众多的CHECKPOINT进行merge为ON-DISK-IMAGE,

同步日志

该方法在主从节点上同时维护上述一整套结构,主节点向从节点同步LOG,从节点执行LOG内容,这也是多数分布式系统维护主从状态一致性的做法,就不再多说了。

同步日志变种

这种方法在上面的基础上作了一点改动:从节点不再维护IN-MEMORY、CHECKPOINT、ON-DISK-IMAGE等结构,为了减少从节点的内存和计算开销。主节点除同步LOG外,还将CHECKPOINT、ON-DISK-IMAGE也一并同步至从节点,这样做的缺点是:1. 增加网络负载;2. 从节点在变成新的主以后,还要加载Checkpoint和日志,以便在内存中构造IM-MEMORY状态机,延长了系统不可用时间。一般这种做法不多。

分层设计

严格来说,这种属于偷懒做法:将可靠性交给底层的文件系统来保证,即存放LOG的不再是单机文件系统,而是分布式文件系统。BigTable、HBase等都是采用这种做法。BigTable底层使用了GFS,而HBase底层则使用了HDFS。

参考

  • PacificA: Replication in Log-Based Distributed Storage Systems

文章被以下专栏收录