Raft只读操作实现要点

论坛 期权论坛 脚本     
已经匿名di用户   2021-12-20 01:35   1391   0

Leader直连

所有的客户端最终都只会链接到Leader。客户端开始时随机挑选一个节点,如果这个节点不是Leader,就通过响应告诉客户端Leader的地址,然后客户端再去连Leader。

Follower转发

客户端可以链接任意节点,客户端的指令会被Follower转发到Leader来执行。

只读操作也必须经过majority确认

只读操作一般只需要读取当前节点的状态机就可以了。但是存在网络分区的情况会导致当前的节点数据严重落伍。被分区隔离开来的Leader和Follower全然不知整个集群已经经过了新一轮的选举,自己的数据已经严重落伍了。所以不论客户端连接的是Follower还是Leader,都不能直接读取状态机来处理只读操作。

Raft对只读操作的处理办法是

  1. 只读请求最终也必须依靠Leader来执行,如果是Follower接收请求的,那么必须转发
  2. 记录下当前日志的commitIndex => readIndex
  3. 执行读操作前要向集群广播一次心跳,并得到majority的反馈
  4. 等待状态机的applyIndex移动过readIndex
  5. 通过查询状态机来执行读操作并返回客户端最终结果。

在步骤1到步骤2之间,如果Leader刚刚当选,还必须等待no-op操作同步完成。

上面的步骤看起来很复杂,其中最重要的就是心跳广播,这是为了确认当前集群没有被网络分区。

只读操作没那么快

因为只读操作也要经过一次RPC,所以它并没有我们想想的那么快,它可能和写操作性能差不多。所以并不能通过扩展节点数量来得到整体集群读性能的提升,甚至不升反降。

折中的方案就是单独提供一个特殊的只读指令,在一致性要求不高的场合使用这个特殊指令。这样就可以通过扩展集群数量来提升读性能。但是在遇到网络分区时会导致数据陈旧的问题,要看业务场景是否可以容忍。

只读操作的进一步优化

标准的强一致只读操作是完全是在Leader端进行的。这里可以做一步改进让只读操作主要在Follower端进行。

  1. Follower接收到只读指令后,向leader索要当前的readIndex值。
  2. Follower端等待自身的状态机的applyIndex移动过readIndex。
  3. Follower端通过查询自身的状态机来执行读操作并返回客户端最终结果。

leader向follower返回readIndex也不是简单的直接返回,而是需要重复前面的标准步骤1~步骤3,来确认网络没有分区。这还是一次RPC操作,无法省略。但是毫无疑问,它分担了leader的压力,可以让leader有更多的资源来处理自身的读写操作。

只读操作的终极优化

前面提到的方法无法避免额外的RPC操作来确认网络没有被分区,所以性能没有非常明显的得到优化。 Raft的论文中提到了另一种可以大幅提升只读操作性能的优化方案,同时还告诫读者不到万不得已不要使用。

因为这个额外的RPC操作只是为了确认网络没有被分区,当前节点有无被孤立,新的leader是否有产生。而新leader的产生是需要经历一次完整的选举过程的,这个选举过程有一定的时间才能完成。所以这个分区的状态是可以被缓存一小段时间的。这段时间内不可能出现leader的变更。

leader收到majority的心跳响应后,在接下来的一小段时间里不必去广播心跳来确认分区情况,而可以直接查询当前节点的状态机返回客户端结果。

之所以作者不推荐折中方案是考虑到时钟漂移。比如一个定时任务在系统非常繁忙的时候是不能得到准时执行的,会产生偏差,偏差的时间取决于系统的繁忙程度、Full GC是否正在进行、虚拟机是否正在迁移等特殊情况。

关注公众号「码洞」,加入我们一起进阶Raft协议算法

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:81
帖子:4969
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP