一、什么是快照
快照(snapshot)是最简单的压缩方式。在快照中,全部的当前系统状态都被写入到快照中,存储到持久化的存储中,然后在那个时刻之前的全部日志都可以被丢弃。
打个比方,像Redis这样的KV系统,系统的当前状态就是当前所有key的值及过期时间,把这些信息全部写入到磁盘中就是快照。
二、Raft算法中为什么需要快照
Raft算法是通过日志来保证节点最终一致的,而日志是持续增加的,对于一个7*24小时运行的系统,日志会一直增加,这样导致几个问题:
1、磁盘占用空间过大;
2、新的节点加入进来后,需要同步的日志太多,进一步影响系统的可用性;
还有1点不是Raft算法中本身的功能,就是恢复数据,即一个误操作需要回滚,则需要回放从前到后所有日志,这个时间会非常长,这时如果有快照就可以快速恢复了。
mysql binlog、Redis的aof文件其实就相当于快照,只不过这些系统没有实现Raft算法。
三、与快照相关的RPC
1、安装快照 RPC(InstallSnapshot RPC)
参数 | 说明 |
---|---|
term | Leader的任期 |
leaderId | Leader的id |
lastIncludedIndex | 快照中包含的最后日志条目的索引值 |
lastIncludedTerm | 快照中包含的最后日志条目的任期号 |
offset | 分块在快照中的偏移量 |
data[] | 快照块的原始数据 |
done | 如果是最后一块数据则为真 |
返回值 | 描述 |
---|---|
term | Follower的当前任期号 |
对于接收方规则如下
- 如果term < currentTerm立刻回复
- 如果是第一个分块(offset 为 0)则创建新的快照
- 在指定的偏移量写入数据
- 如果 done为 false,则回复并继续等待之后的数据
- 保存快照文件,丢弃所有存在的或者部分有着更小索引号的快照
- 如果现存的日志拥有相同的最后任期号和索引值,则后面的数据继续保留并且回复
- 丢弃全部日志
- 能够使用快照来恢复状态机(并且装载快照中的集群配置)
这些规则大部分应该好理解,部分规则解释下:
5、保存快照文件,丢弃所有存在的或者部分有着更小索引号的快照
假如说Follower已经有快照了,并且快照最后索引为1000,而新的快照的索引为2000,则将前面的快照丢弃
6、如果现存的日志拥有相同的最后任期号和索引值,则后面的数据继续保留并且回复
意思说接收节点如果有相应的日志了,则后面的日志保留,此消息可以直接回复。
打个比方,如果Follower B的索引已经到2002,此索引对应的term为102,其中2000索引的term为101,如果这时收到一个安装快照的消息,最后1条的term为101,最后1条的索引为2000,通过对比发现此日志已经存在节点上,并且Term也对的上,因此2001之后的日志保留。
7、丢弃全部日志
上面条件满足后,将快照保存到本地,本地所有日志全部丢弃。
当然前提是前面的条件都不满足,具体不细述。
8、能够使用快照来恢复状态机(并且装载快照中的集群配置)
恢复状态机就不用说了,直接拿快照恢复状态机的数据,举例来说KV系统,发送的快照如果只有a=1, b=2这样的状态,即把所有数据清空,只保留上面2条数据。
并且装载快照中的集群配置,意思是说快照还包含集群配置信息,主是要为了支持集群成员更新;
所以快照必须以下信息:
最后一条日志的Index;
最后一条日志的Term;
生成快照时的集群配置信息;
状态机数据;
四、其它细节
1、何时生成快照
这个Raft算法并没有规定,看应用自己实现,像etcd是10000日志后产生1次快照,需要根据实际条件选择。
2、谁生成日志快照
Raft算法并没有规定谁可以生成,即谁都可以生成,即符合条件1就可以生成,主要是为了切换为Leader的时候可以快速应对新节点添加数据的情况。因为只有数据一致,谁生成都是一样的。