etcd系列-----raft协议:理论基础篇(三)-------快照

通过前面的描述可知, 随着客户端与集群不断地交互,每个节点上的日志记录会不断增加,但是服务器的空间都是有限的,日志量不能无限制地增长。另外,在节点重启时会重放日志记录,如果日志记录过多,则需要花费较长的时间完成重放操作。这就需要压缩和清除机制来减少日志量,从而避免上述情况。定期生成快照是最常见也是最简单的压缩方法。在创建快照文件时,会将整个节点的状态进行序列化,然后写入稳定的持久化存储中,这样,在该快照文件之前的日志记录就可以全部丢弃了。例如,集群中变量a的值为100, 客户端发送了一个更新请求将变量a更新为13, 经过前面描述的日志复制过程之后,该请求对应的日志记录最终被提交并应用到集群中的每个节点中。此时每个节点中维护的变量a 都是13, 而a=13 这条日志记录就无须继续保留。在ZooKeeper、 Chubby和etcd中都有类似上述的快照处理逻辑,这里只是介绍创建快照文件和压缩日志的基本逻辑,在后面的章节会具体介绍其实现。

在快照中除了节点当前的数据状态,还包含了其最后-条日志记录的任期号和索引号,如图所示,该快照包含了6条日志记录,在快照的元数据中记录了第6条日志记录的任期号和索引号,在生成快照文件之后,即可将l~6条日志记录丢弃了。

一般情况下,集群中的每个节点都会自己独立、定时地创建快照,在其状态恢复时,都会使用自己本地最新的快照数据。如果Follower节点长时间宕机(或是刚刚加入集群的新节点),就有可能导致其日志记录远远落后于当前的Leader节点,与此同时,Leader节点中陈旧的日志记录己被删除了。 在这种场景下,为了将该Follower节点恢复到正确的状态,Leader节点会将快照发送给该Follower节点,Follower节点会使用该快照数据进行状态恢复。当Leader节点需要向Follower节点发送快照时,会发送一种特殊的消息类型(快照消息〉。etcd的网络层为了高效地传输消息,会将快照的发送与普通消息(AppendEntries消息、心跳消息等)的发送分开在不同的消息通道中完成,在后面介绍etcd网络层时会详细介绍。

当Follower节点接收到该快照消息时,必须决定如何处理己存在的日志记录,在快照中之所以保留前面介绍的一些元数据,其作用之一就是为了在Follower节点收到快照之后进行一致性检查。一般情况下,快照己包含了该Follower节点中不存在的日志记录,此时Follower节点直接丢弃其所有的日志记录,因为这些日志最终会被Leader传递来的快照所代替。如果Follower节点接收到的快照只包含了自己本地日志的一部分,那么被该快照所包含的全部日志记录会被全部删除,但是快照之后的日志则会保留。

或许你可能会考虑过另一种替代方案,即只有Leader节点创建快照,然后发送给所有的Follower 节点。但是该方案有几个缺点:首先就是快照数据会比较大,并且发送快照数据是比较浪费网络带宽的,也比较耗时,这显然比Follower节点从本地直接加载要耗时很多;其次就是Leader的实现会更加复杂。

在Raft协议中,每个节点都会创建快照,所以创建快照的时机决定了快照的性能。如果创建快照过于频繁,那么就会消耗大量的资源,导致每个节点的性能下降;如果创建快照的频率过低,那么两次创建快照之间积累的日志记录会比较多,快照就无法为节点节约内存等资源。所以我们要在两者之间进行权衡,常见的策略是当日志记录个数达到一个固定阀值的时候(目前默认是10000条),就触发一次创建快照的操作,生成相应的快照文件,我们可以通过调节该阀值来控制创建快照的频率。

发布了48 篇原创文章 · 获赞 9 · 访问量 1万+

猜你喜欢

转载自blog.csdn.net/cyq6239075/article/details/105319542