3

在 raft 的论文文档第 6.4 章中,它给出了绕过 Raft 日志进行只读查询并仍然保持线性化的步骤:

  1. 如果领导者尚未将其当前任期内的条目标记为已提交,则它会一直等到这样做。领导者完整性属性保证领导者拥有所有已提交的条目,但在其任期开始时,它可能不知道那些是哪些。要找出答案,它需要从其任期内提交一个条目。Raft 通过让每个领导者在其任期开始时将一个空白的 no-op 条目提交到日志中来处理这个问题。一旦这个无操作条目被提交,领导者的提交索引在其任期内将至少与任何其他服务器一样大。
  2. 领导者将其当前提交索引保存在局部变量 readIndex 中。这将用作查询操作所针对的状态版本的下限。
  3. 领导者需要确保它没有被它不知道的新领导者取代。它发出新一轮的心跳并等待大多数集群的确认。一旦收到这些确认,领导者就知道在发送心跳的那一刻,领导者不可能存在更长的任期。因此,当时 readIndex 是集群中任何服务器所见过的最大提交索引。
  4. 领导者等待其状态机至少前进到 readIndex;这足以满足线性化。
  5. 最后,领导者针对其状态机发出查询,并用结果回复客户端。

我的问题:

a)对于第1步,是否仅适用于刚刚选举出领导者时的情况?因为只有新领导者没有为当前任期提交条目。并且由于 no-op 条目对于找出当前提交的条目是必要的,那么实际上这一步在选举完成时总是需要的,但不仅限于只读查询吗?换句话说,通常,当领导者活动一段时间时,它必须在其任期内提交条目(包括无操作条目)。

b) 对于第 3 步,这是否意味着只要领导者需要提供只读查询,就会发送一个额外的心跳,而不管当前未完成的心跳(已发送但尚未收到主要响应)或下一个预定的心跳?

c) 对于第 4 步,是否仅适用于追随者(对于追随者帮助卸载只读查询处理的情况)?因为在领导者上,提交的索引已经意味着它被应用于本地状态机。

总而言之,正常情况下,leader(活跃一段时间)只需要做第3步和第5步,对吧?

4

1 回答 1

4

a : 确实只有第一次选举出leader时才会出现这种情况。在实践中,当接收到只读查询时,您检查是否已从领导者的当前任期和队列中提交条目,如果没有,则拒绝查询。

b:在实践中,大多数实现批处理只读查询以提高效率。您不需要发送许多并发心跳。如果心跳未完成,领导者可以在心跳完成后将任何新的读取排入队列以进行评估。一旦心跳完成,如果有任何其他查询排队,那么领导者将启动另一个心跳。这具有批处理线性化只读查询以提高效率的效果。

c :领导者的索引(它的状态机的索引)总是等价于它的 是正确的。确实,这就是为什么Raft 中首先有索引的原因。领导者不一定必须在提交索引的同时同步应用索引。这确实是特定于实现的。在实践中,Raft 实现通常在不同的线程中应用条目。因此,可以提交一个条目,然后将其排入队列以应用到状态机。一些实现将条目放在队列中以应用于状态机并允许状态机拉取lastAppliedcommitIndexlastApplied该队列中的条目以状态机自己的速度应用,因此未指定何时应用条目。在领导者提交的最后一个命令之后应用只读查询至关重要。

另外,您问这是否仅适用于追随者。线性化查询只能通过领导者进行评估。我想有一些算法可以用来对追随者进行线性化读取,但效率低下。追随者只能保持查询的顺序一致性。在这种情况下,服务器在评估操作时使用状态机的索引响应客户端操作。客户端在每次操作中发送他们最后收到的索引,当服务器接收到操作时,它使用相同的算法来确保其状态机的lastApplied索引至少与客户端的索引一样大。这对于确保客户端在切换服务器时不会及时看到状态返回是必要的。

如果您想支持来自单个客户端的并发操作的 FIFO 一致性,那么除了 Raft 文献中描述的内容之外,只读查询还有一些其他复杂性。其中一些在 Copycat 的架构文档中进行了描述。

于 2016-05-13T18:22:58.013 回答