Raft学习笔记(二)——logIndex唯一性问题&日志空洞问题

810 阅读3分钟

我正在参加「掘金·启航计划」

这是笔者Raft学习笔记的第二篇,是学习完核心算法后的两点思考。

logIndex唯一性

Raft学习笔记(一)中提到,Raft的日志号logIndex是全局单调递增的。但却需要logIndex和term才能唯一确定一条日志。

既然logIndex全局递增,那就是全局不重复。为什么还需要term?term是否是多余的?

term当然不是多余的,Raft不会这么蠢。

Raft学习笔记(一)提到,leader在当选之后,是不会覆盖或删除自己的日志,而是要求follower覆盖掉与leader冲突的日志。

raft-图7.png

在上图中,同样的logIndex=11,有3个不同的term,此时就说明了term+logIndex才能唯一确定一条日志。而当leader当选后,在第8个term,它会覆盖掉此时logIndex=11及其之后的日志。

结论

所以关于日志唯一性的问题,得出几个自己的结论:

  • 任意节点上,对于该节点上已提交的日志,日志号logIndex可以唯一确定一条日志,因为之后不会再被覆盖了。好像一句废话。
  • 可是如果把局限放宽,在全局,对于已经提交的日志,logIndex无法唯一确定一条日志,比如图中term=6&logIndex=8的日志是肯定被提交了,但同样的logIndex=8却还有f节点上term=3的那条日志。所以放宽到全局,只能是term+logIndex才能唯一确定一条日志。

日志空洞

raft-图7.png

还是拿这个图来说吧,可以明显看到log index在所有节点上都是连续的,即使可能被覆盖,即使经过多次leader切换,都依然是连续递增的。这个就是没有日志空洞。

如何实现

原因我理解就是为了保证算法的简单性、易理解性。来看几条Raft的规则:

  • 强leader:Raft的最大特性就是拥有长生命周期的强leader,日志复制只能由leader流向follower,则被选出的leader就必须拥有完整的已提交日志
  • 选主算法:Raft的选主,只需要比较term和logIndex大小就能快速选出leader
  • 日志复制:Raft的日志复制,由leader流向follower,顺序复制,顺序提交

这几条规则就保证了无日志空洞:

  • 同一任期内无日志空洞。同一任期内,logIndex连续递增,在一条leader和follower的复制链路中,日志顺序复制,所以无空洞。
  • 任期切换无空洞。新leader当选后,自己任期内的第一条日志,是上一条日志logIndex+1,然后强制follwer复制并覆盖有分歧的日志。所以任期切换时也不会有空洞。

缺点?

不允许日志空洞可能会导致性能的损失。在高并发大数据量的情况下,Raft顺序复制可能会成为瓶颈。对应的优化方法可能有:

  • Multi-Raft:对数据做分片,每个分片是一组Raft集群,同一分片内数据顺序复制。
  • 批量复制:对连续的日志批量复制到follower,能极大减少RPC数量,提高吞吐。但提交应该还是要保证顺序性的

允许日志空洞会带来复杂度

如果允许日志空洞,可能有哪些需要考虑的问题:

  • 如何保证选出的leader的日志是完整的?简单的term和logIndex大小比较也许就不行了。难道leader选出之后还需要从follower补充自己的数据吗?
  • 乱序提交对数据正确性的影响。允许日志空洞,则允许乱序复制,是否允许乱序提交?如果乱序提交,状态机的正确性如何保证?

这些问题想想就头大。

总结

如果允许日志空洞,会极大增加一致性算法的复杂度,而Raft用简单的规则避免了日志空洞,提高了易理解度,降低了复杂度。