Raft如何解决日志无限增长的问题?
为什么会产生这个问题?
Raft 将操作包装成为了日志,集群每个节点都维护了一个不断增长的日志序列,状态机只有通过重放日志序列来得到。但由于这个日志序列可能会随着时间流逝不断增长,因此我们必须有一些办法来避免无休止的磁盘占用和过久的日志重放。
解决方案:日志压缩
快照(Snapshot)是一种常用的、简单的日志压缩方式,ZooKeeper、Chubby 等系统都在用。
简单来说,就是将某一时刻系统的状态 dump 下来并落地存储,这样该时刻之前的所有日志就都可以丢弃了。
在 Raft 中我们只能为 committed 日志做 snapshot
快照包含
1、日志的元数据:最后一条被该快照 apply 的日志 term 及 index<br>2、状态机:前边全部日志 apply 后最终得到的状态机
Raft如何解决成员变更的问题?
为什么会产生这个问题?
一个 Raft 集群不太可能永远是固定几个节点,总有扩缩容的需求,或是节点宕机需要替换的时候。直接更换集群成员可能会导致严重的脑裂问题。Raft 给出了一种安全变更集群成员的方式。
解决方案:集群成员变更(Cluster membership change)
Raft 使用一种两阶段方法平滑切换集群成员配置来避免脑裂。
“选举超时时间”该如何制定?如果所有节点在同一时刻启动,经过同样的超时时间后同时发起选举,整个集群会变得低效不堪,极端情况下甚至会一直选不出一个主节点。
Raft 巧妙的使用了一个随机化的定时器,让每个节点的“超时时间”在一定范围内随机生成
Raft如何处理日志不至的情况?
Raft 强制要求 follower 必须复制 leader 的日志集合来解决不一致问题。leader 从来不会覆盖或者删除自己的日志,而是强制 follower 与它保持一致。
follower 节点上任何与 leader 不一致的日志,都会被 leader 节点上的日志所覆盖。
要使得 follower 的日志集合跟自己保持完全一致,leader 必须先找到二者间最后一次达成一致的地方。因为一旦这条日志达成一致,在这之前的日志一定也都一致
Leader 针对每个 follower 都维护一个 next index,表示下一条需要发送给该follower 的日志索引。当一个 leader 刚刚上任时,它初始化所有 next index 值为自己最后一条日志的 index+1。但凡某个 follower 的日志跟 leader 不一致,那么下次 AppendEntries RPC 的一致性检查就会失败。在被 follower 拒绝这次 Append Entries RPC 后,leader 会减少 next index 的值并进行重试。<br>
针对每个 follower,一旦确定了 next index 的值,leader 便开始从该 index 同步日志,follower 会删除掉现存的不一致的日志,保留 leader 最新同步过来的。