《云计算技术系列丛书 云原生分布式存储基石: etcd深入解析》—1.4.4日志压缩与快照
1.4.4日志压缩与快照
在实际的系统中,Raft节点上的日志记录不可能无限制地增加下去。一方面日志记录会对节点的存储空间造成压力,另一方面当Raft节点重启时需要花费大量的时间进行日志回放(replay),进而影响系统的可用性。
使用快照进行日志压缩的方法并不少见,ZooKeeper和Chubby中都有应用。在快照系统中,系统的全部状态都以快照的形式写入持久化存储,然后删除那个时间点之前的全部日志。下文将详细介绍Raft的快照原理。
图1-21展示了Raft快照的基本原理,一个Raft节点从1号到5号位置的日志条目生成了一个新的快照文件。从图1-21可以看出,Raft的快照文件具有如下特点。
每个节点独立创建,只包含已经被提交的日志条目。
存储了节点某一时刻复制状态机的状态。
全量式,非增量式的(即使数据没有改变)。
在快照中存储少量元数据,比如,被快照取代的最后一个日志条目的索引位置和对应的任期号。
图1-21 Raft快照原理图
Raft快照元数据中会存储被快照取代的最后一个日志条目的索引位置和对应的任期号,这是为了支持快照后第一个日志条目的AppendEntries RPC一致性检查(因为这个日志条目需要它前一个日志条目的索引值和任期号)。为了支持集群成员关系列表的更新(将在下文展开讨论),快照文件也会将最后一次的配置作为最后一条日志保持。一旦Raft节点成功生成快照文件,就可以删除最后的索引位置及其之前的所有日志和快照了。
尽管在通常情况下,Raft节点之间都是独立创建快照的,但是Leader偶尔也需要向一些过于落后的Follower发送快照。这种情况通常发生在Leader因为做快照删除了还未发送给Follower的日志条目的情况下。当然,其实与Leader保持同步的Follower通常不需要Leader做这个操作,需要Leader发送的对象往往是一个运行非常缓慢的Follower或者是一个新加入集群的节点。因此,通过网络传输快照文件也是让Follower尽快同步Leader状态的一种方式。但是,当快照文件较大时,就不能忽视网络和磁盘的开销了。
Raft算法的InstallSnapshot RPC实现了Leader和Follower之间发送和接收快照文件的过程。当有些快照文件过大时,需要对其进行分块传输。对于每个节点,领导人总是顺序执行InstallSnapshot RPC,即顺序发送快照分块。
InstallSnapshot RPC的参数说明如表1-5所示。
InstallSnapshot RPC的返回值说明如表1-6所示。
接收者实现快照的步骤具体如下。
1)与前文介绍的RPC类似,如果term < currentTerm,则立刻返回currentTerm,即如果节点的当前任期号大于Leader的任期号,则拒绝该快照;否则执行步骤2)。
2)如果是第一个分块(offset为0),则新建一个快照。
3)在指定偏移量处将分块数据写入快照文件,并响应Leader。
4)如果done是false,则表示快照文件尚未传输完成,需要继续等待更多的数据块。
5)当接收到的done是true时,保存该快照文件,丢弃本地的lastIncluded-Index值较小(较旧)的现存快照。
6)节点将根据快照包含的最后一条日志的索引值和任期号搜索与之匹配的日志项,如果存在,则继续保留后面该日志项之后的日志,前面的日志项将全部删除。
7)应用快照内容重置节点状态机,并且加载快照文件中的集群配置信息。
以上便是关于InstallSnapshot RPC的一个简要概述。快照分块除了要便于传输之外,还可作为每个领导人的心跳包,Leader每次接收到快照分块都需要重置一次选举超时定时器。
与Raft算法的其他操作都是基于领导人的原则不同,快照是由各个节点独立生成的。这种快照的方式违背了Raft的强领导人原则—因为Follower可以在没有领导人的情况下生成快照。Raft算法的作者认为这种“违背”是能够接受的。因为领导人的存在是为了解决达成一致性时产生的冲突,但当快照创建时一致性已经达成了,此时不存在冲突,所以即使没有领导人也可以生成快照文件。数据依旧保持从Leader流向Follower不变,只是快照允许Follower重新组织它们的数据而已。
事实上,Raft算法的作者在他的论文中提到过,他们曾经考虑过一种基于领导人的快照方案,即只有Leader创建快照,然后发送给所有的Follower。但是这样做有两个明显缺点。
1)发送快照会浪费网络带宽并且增加了快照处理的时延。每个Follower本地已经拥有了产生快照需要的所有信息,从本地状态创建快照显然比通过网络接收别人发来的要经济得多。
2)增加领导人实现的复杂性。例如,领导人需要在发送快照的同时并行地将新的日志条目发送给跟随者,这样才不会阻塞新的客户端请求。
快照操作会对系统的性能造成一定的影响,集群管理员需要决定创建快照的时机。如果快照操作太频繁,则会消耗大量的I/O带宽和CPU资源。如果超过时间不做快照,那么节点存储空间就有被日志文件耗尽的风险,而且Raft节点一旦重启就需要回放大量日志,进而影响系统的可用性。我们推荐的解决方法是当日志达到某个固定的大小时做一次快照。
另外,一次写入快照文件可能会消耗较长的时间,如果不希望影响正常日志条目的复制,则可以通过使用写时复制(copy-on-write)的技术来解决。这样就能在接受写日志请求的同时而不影响正在被写入的快照文件。另外,操作系统的写时复制技术的支持(如Linux上的fork)可以被用来创建完整的状态机内存快照。
后面的章节会专门介绍etcd的快照实现细节。
- 点赞
- 收藏
- 关注作者
评论(0)