raft:关于只读查询的一些问题

kin*_*luo 3 raft

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

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

我的问题:

\n\n

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

\n\n

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

\n\n

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

\n\n

总而言之,正常情况下,领导者(活跃一段时间)只需要执行步骤3和步骤5,对吗?

\n

kuu*_*ujo 5

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

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

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

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

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