MIT6.824/lecture-07-raft2/7.3-hui-fu-jia-su-backup-acceleration.md
2022-01-25 02:41:31 +00:00

6.8 KiB
Raw Permalink Blame History

7.3 快速恢复Fast Backup

在前面7.1介绍的日志恢复机制中如果Log有冲突Leader每次会回退一条Log条目。 这在许多场景下都没有问题。但是在某些现实的场景中至少在Lab2的测试用例中每次只回退一条Log条目会花费很长很长的时间。所以现实的场景中可能一个Follower关机了很长时间错过了大量的AppendEntries消息。这时Leader重启了。按照Raft论文中的图2如果一个Leader重启了它会将所有Follower的nextIndex设置为Leader本地Log记录的下一个槽位7.1有说明。所以如果一个Follower关机并错过了1000条Log条目Leader重启之后需要每次通过一条RPC来回退一条Log条目来遍历1000条Follower错过的Log记录。这种情况在现实中并非不可能发生。在一些不正常的场景中假设我们有5个服务器有1个Leader这个Leader和另一个Follower困在一个网络分区。但是这个Leader并不知道它已经不再是Leader了。它还是会向它唯一的Follower发送AppendEntries因为这里没有过半服务器所以没有一条Log会commit。在另一个有多数服务器的网络分区中系统选出了新的Leader并继续运行。旧的Leader和它的Follower可能会记录无限多的旧的任期的未commit的Log。当旧的Leader和它的Follower重新加入到集群中时这些Log需要被删除并覆盖。可能在现实中这不是那么容易发生但是你会在Lab2的测试用例中发现这个场景。

所以为了能够更快的恢复日志Raft论文在论文的5.3结尾处对一种方法有一些模糊的描述。原文有些晦涩在这里我会以一种更好的方式尝试解释论文中有关快速恢复的方法。这里的大致思想是让Follower返回足够的信息给Leader这样Leader可以以任期Term为单位来回退而不用每次只回退一条Log条目。所以现在在恢复Follower的Log时如果Leader和Follower的Log不匹配Leader只需要对每个不同的任期发送一条AppendEntries而不用对每个不同的Log条目发送一条AppendEntries。这只是一种加速策略当然或许你也可以想出许多其他不同的日志恢复加速策略。

我将可能出现的场景分成3类为了简化这里只画出一个LeaderS2和一个FollowerS1S2将要发送一条任期号为6的AppendEntries消息给Follower。

  • 场景1S1没有任期6的任何Log因此我们需要回退一整个任期的Log。

  • 场景2S1收到了任期4的旧Leader的多条Log但是作为新LeaderS2只收到了一条任期4的Log。所以这里我们需要覆盖S1中有关旧Leader的一些Log。

  • 场景3S1与S2的Log不冲突但是S1缺失了部分S2中的Log。

可以让Follower在回复Leader的AppendEntries消息中携带3个额外的信息来加速日志的恢复。这里的回复是指Follower因为Log信息不匹配拒绝了Leader的AppendEntries之后的回复。这里的三个信息是指

  • XTerm这个是Follower中与Leader冲突的Log对应的任期号。在之前7.1有介绍Leader会在prevLogTerm中带上本地Log记录中前一条Log的任期号。如果Follower在对应位置的任期号不匹配它会拒绝Leader的AppendEntries消息并将自己的任期号放在XTerm中。如果Follower在对应位置没有Log那么这里会返回 -1。
  • XIndex这个是Follower中对应任期号为XTerm的第一条Log条目的槽位号。
  • XLen如果Follower在对应位置没有Log那么XTerm会返回-1XLen表示空白的Log槽位数。

我们再来看这些信息是如何在上面3个场景中帮助Leader快速回退到适当的Log条目位置。

  • 场景1。FollowerS1会返回XTerm=5XIndex=2。LeaderS2发现自己没有任期5的日志它会将自己本地记录的S1的nextIndex设置到XIndex也就是S1中任期5的第一条Log对应的槽位号。所以如果Leader完全没有XTerm的任何Log那么它应该回退到XIndex对应的位置这样Leader发出的下一条AppendEntries就可以一次覆盖S1中所有XTerm对应的Log
  • 场景2。FollowerS1会返回XTerm=4XIndex=1。LeaderS2发现自己其实有任期4的日志它会将自己本地记录的S1的nextIndex设置到本地在XTerm位置的Log条目后面也就是槽位2。下一次Leader发出下一条AppendEntries时就可以一次覆盖S1中槽位2和槽位3对应的Log。
  • 场景3。FollowerS1会返回XTerm=-1XLen=2。这表示S1中日志太短了以至于在冲突的位置没有Log条目Leader应该回退到Follower最后一条Log条目的下一条也就是槽位2并从这开始发送AppendEntries消息。槽位2可以从XLen中的数值计算得到。

这些信息在Lab中会有用如果你错过了我的描述你可以再看看视频Robert教授说的

对于这里的快速回退机制有什么问题吗?

学生提问:这里是线性查找,可以使用类似二分查找的方法进一步加速吗?

Robert教授我认为这是对的或许这里可以用二分查找法。我没有排除其他方法的可能我的意思是Raft论文中并没有详细说明是怎么做的所以我这里加工了一下。或许有更好更快的方式来完成。如果Follower返回了更多的信息那是可以用一些更高级的方法例如二分查找来完成。

为了通过Lab2的测试你肯定需要做一些优化工作。我们提供的Lab2的测试用例中有一件不幸但是不可避免的事情是它们需要一些实时特性。这些测试用例不会永远等待你的代码执行完成并生成结果。所以有可能你的方法技术上是对的但是花了太多时间导致测试用例退出。这个时候你是不能通过全部的测试用例的。因此你的确需要关注性能从而使得你的方案即是正确的又有足够的性能。不幸的是性能与Log的复杂度相关所以很容易就写出一个正确但是不够快的方法出来。

学生提问:能在解释一下这里的流程吗?

Robert教授这里Leader发现冲突的方法在于Follower会返回它从冲突条目中看到的任期号XTerm。在场景1中Follower会设置XTerm=5因为这是有冲突的Log条目对应的任期号。Leader会发现我的Log中没有任期5的条目。因此在场景1中Leader会一次性回退到Follower在任期5的起始位置。因为Leader并没有任何任期5的Log所以它要删掉Follower中所有任期5的Log这通过回退到Follower在任期5的第一条Log条目的位置也就是XIndex达到的。