raft-kv项目补充

目录

项目总结

日志压缩

Raft协议是如何保证安全性的?

怎么做到的?要做到,必须遵循以下三点

怎么避免日志覆盖问题?

第一种:提交的记录是在当前任期

第二种:提交的记录是在前序任期

解决办法

怎么避免网络分区问题?

怎么避免命令可能被执行两次的问题?

多节点成员变更出现的问题

为啥选用 RocksDB?

常见面试题

Raft数据一致性如何实现?

Raft和Paxos的区别和优缺点?

Raft prevote机制?

Raft里面怎么保证数据被commit?leader宕机了会怎样,之前的没提交的数据会怎样?


项目总结

此次我们语言部分使用 Java,RPC 网络通信框架使用的是SOFA-Bolt,后期加入了自研的RPC模块,底层 KV 存储使用的是 RocksDB,其中核心的 Raft 则由我们自己实现(如果不自己实现,那这个项目没有意义)。 注意,该项目将舍弃一部分性能和可用性,以追求尽可能的强一致性

Raft 为了算法的可理解性,将算法分成了 4 个部分。

  1. leader 选举
  2. 日志复制
  3. 动态成员变更
  4. 底层使用RPC框架通信(法一:蚂蚁金服 SOFA-Bolt,法二:自己实现的RPC框架)

leader 的选举

leader 的选举则通过比较每个节点的逻辑时间(term)大小,以及日志下标(index)的大小。

日志复制

日志复制可以说是 Raft 核心的核心,说简单点,Raft 就是为了保证多节点之间日志的一致。当日志一致,我们可以认为整个系统的状态是一致的。这个日志你可以理解成 mysql 的 binlog。

Raft leader 节点会将客户端的请求都封装成日志,发送到各个 follower 中,如果集群中超过一半的 follower 回复成功,那么这个日志就可以被提交(commit),这个 commit 可以理解为 ACID 的 D ,即持久化。当日志被持久化到磁盘

日志压缩

在实际的系统中,不能让日志无限增长,否则系统重启时需要花很长的时间进行回放,从而影响可用性。Raft采用对整个系统进行snapshot来解决,snapshot之前的日志都可以丢弃(以前的数据已经落盘了)

  每个副本独立的对自己的系统状态进行snapshot,并且只能对已经提交的日志记录进行snapshot

当Leader要发给某个日志落后太多的Follower的log entry被丢弃,Leader会将snapshot发给Follower。或者当新加进一台机器时,也会发送snapshot给它。

  做snapshot既不要做的太频繁,否则消耗磁盘带宽, 也不要做的太不频繁,否则一旦节点重启需要回放大量日志,影响可用性。推荐当日志达到某个固定的大小做一次snapshot

  做一次snapshot可能耗时过长,会影响正常日志同步。可以通过使用copy-on-write技术避免snapshot过程影响正常日志同步。

Raft协议是如何保证安全性的?

安全性:几乎所有的日志复制同步系统都会对安全性有所要求,一旦某个状态机接收了一条日志记录并执行,我们必须保证不存在其他的状态机执行不同的命令。

为了达成总体的安全性要求,Raft 实现了一个安全属性,一旦领导者决定某个特定记录已提交,那么 Raft 就需要保证该条记录会出现在它所有未来领导者的日志记录中,并且也处于已提交状态。

怎么做到的?要做到,必须遵循以下三点

1 领导者永远不会覆盖自己的日志记录,它只会追加

2 为了到达已提交的状态,记录必须在领导者日志中,这样就不会有其他值会被提交

3 日志记录必须在发送给状态去执行之前被提交

如何保证2?

通过选举协议:赢得选举的服务器可以保证比大多数投票者有更完整的日志记录

怎么避免日志覆盖问题?

  • 第一种:提交的记录是在当前任期

在第二个任期上,领导者(S1)刚成功调用 AppendEntries 至 S3 ,此时它发现记录已在大多数服务器上存储,随即标记该记录是已提交的,并将其传送给状态机。此时这条记录是安全的,因为下一任期的领导者必须认定该记录的已提交状态 ,原因是领导者只会在S1,S2,S3中产生。

  • 第二种:提交的记录是在前序任期

                  

 领导者在任期 2 只复制了两台服务上的日志记录,随后任期 3 的领导(S5)出于某些原因没有关注到这些记录,在它本地创建了一些记录,然后崩溃了。然后在任期 4 上,领导者(S1)试图将其他服务器上的日志内容与它自己的达成一致。所以它让服务器 S3 复制了它自己 Term-2 记录,在这个点上,该记录已被领导者知道存于大多数服务器上,但该记录并没有安全的被提交。因为此时 S1 可能出现崩溃,S5 成为领导者,因为它的前序任期值 3 较大,所以它可以获得来自于 S2、S3、S4 的投票,如果它当选,那么它会试图将自己的日志推到其他的服务器,这也就意味着从 S1 - S4 下标位置索引 3 开始的所有记录都会被删除。所以此时我们还无法认定记录 3 是否已经提交。

  • 解决办法

到目前为止只要领导者发现记录已存于大多数服务器,那么它就认为该记录已被提交。但是为了保证安全性,我们需要增加另一条规则。除了上述规则,领导者必须能看见至少有一条来自于它本任期内的记录也存于大多数服务器 。

如果领导者完成了记录 3-2 的复制,它此时还无法提交该记录并将其发送给状态机,取而代之的是,它必须等待直到它当前任期内的第一条记录(4-4)提交并存于大多数的服务器。至此,两条记录才能都发送给状态机

怎么避免网络分区问题?

可以使用任期来防止这种情况的出现。因为每个 RPC 请求都包括发送者的任期号,如果发送者的任期比接收者的要老,那么就表示发送者是过时的,这时接收者会立即拒绝 RPC 请求,并将包括了接收者任期信息的响应发送回发送者,这样当发送者接收到响应时就会意识到,它的任期号是过期的,此时它就会停下并作为跟随者继续运行,同时它还会更新自己的任期号。反之,如果接收者的任期号更老,如果这时接收者不是跟随者,那么它也会停下,并作为跟随者,而且更新它自己的任期号。

怎么避免命令可能被执行两次的问题?

问题原因:领导者会在执行完命令后响应客户端之前发生崩溃,所以命令本身是无法知道自己是否被记录或已被执行。这时客户端就会再次发起请求,这样命令就又被执行了一遍。这是不能被接受的,因为我们要每条命令执行且仅被执行一次

解决办法:让客户端为每条命令生成一个唯一的 ID ,并将其与命令一起发送给领导者,当领导者记录该条命令时,也会包括这个唯一 ID ,但在领导者接受命令之前,它会进行检查,看其他记录中是否已存在相同的 ID ,如果存在相同的,那么它就会知道该条命令请求是多余的,所以它会找到该条记录,并忽略这条新命令,并将老的执行结果返回给客户端

多节点成员变更出现的问题​​​​​​​

在成员变更时,因为无法做到在同一个时刻使所有的节点从旧配置转换到新配置,那么直接从就配置向新配置切换就可能存在一个节点同时满足新旧配置的“超过半数”原则

假设系统集群有三台服务器正在运行,这时我们希望再增加两台服务器,问题是这个切换不能无法同时完成,时间上总会有先有后。而这可能会导致冲突的大多数。因为 S1、S2 可以在某个时候形成旧集群的大多数,并决定领导者。而与此同时,另外三台服务器 S3、S4、S5 已经切至新的配置,它们也形成了该配置状态下的大多数。所以它们也可以决定领导者,确认提交状态。这样就会与 S1、S2 发生冲突。这样,我们就需要使用两段协议。

解决方法:两段协议

第一阶段:Leader收到成员变更请求,Leader在本地生成一个新的log entry,其内容是Cold∪Cnew,代表当前时刻新旧成员配置共存,写入本地日志,同时将该log entry复制至Cold∪Cnew中的所有副本。一旦某个服务器将该新配置日志条目增加到自己的日志中,它就会用该配置来做出未来所有的决策。在此之后新的日志同步需要保证得到Cold和Cnew两个多数派的确认

第二阶段:Leader生成一条新的log entry,其内容是新成员配置Cnew,同样将该log entry写入本地日志,同时复制到Follower上;

最终结果就是:Raft 将第一阶段到中间阶段称为多边共识(joint consensus),在这个阶段中,集群存在新旧两种配置,但是如选举和提交的决策,需要在新旧两个独立的配置状态下达成一致。

为啥选用 RocksDB?

1 采用追加写而不是就地写的方式,这样顺序写会提升写的速度。

内存顺序写速度>内存无序写>IO顺序写>IO无序写

但是会造成数据冗余问题,顾名思义,不会替代原来的数据,只会在后面追加。

2 解决方案

内存通过就地写方式维护一个有序数组,隔一段时间刷新到磁盘中,磁盘会通过分层的方式进行分治合并成一个不冗余的有序数组。

多层压缩合并,有序块存储,然后进行二分查找,提升读的效率。

常见面试题

Raft数据一致性如何实现?

主要是通过日志复制实现数据一致性,leader将请求指令作为一条新的日志条目添加到日志中,然后发起RPC 给所有的follower,进行日志复制,进而同步数据

Raft和Paxos的区别和优缺点?

  • Raft的leader有限制,拥有最新日志的节点才能成为leader,multi-paxos中对成为Leader的限制比较低,任何节点都可以成为leader

  • Raft中Leader在每一个任期都有Term

Raft prevote机制?

 Prevote(预投票)是一个类似于两阶段提交的协议,第一阶段先征求其他节点是否同意选举,如果同意选举则发起真正的选举操作,否则降为Follower角色。这样就避免了网络分区节点重新加入集群,触发不必要的选举操作。

Raft里面怎么保证数据被commit?leader宕机了会怎样,之前的没提交的数据会怎样?

leader会通过RPC向follower发出日志复制,等待所有的follower复制完成,这个过程是阻塞的。

老的leader里面没提交的数据会回滚,然后同步新leader的数据。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值