如何保证分布式系统中的读一致性?

How to ensure consistent reading in distributed system?

在分布式系统中,如果只有一半的节点写入成功,那么后续读取未写入数据的节点就会不一致。如何避免这种情况?

client write --> Node1  v
             --> Node2  v
client read  --> Node3  x(The latest data was not read)

我的计划:

我看到 mongodb 和 elasticsearch 都被标记了,我不知道你在想哪种情况,但这两个数据库非常不同。

对于 mongo,默认情况下不使用副本来提高读取速度,请参阅 https://docs.mongodb.com/manual/core/read-preference, the default reading preferences will only look at primary and excludes all replicas. The writing of Mongo is also to the primary first and the replication will happen asynchronously possibly after the write to primary finishes, see https://docs.mongodb.com/manual/core/replica-set-members/。因此,如果您强制读取次要数据,则无法保证您拥有最新数据。

对于elasticsearch,elasticsearch自然不能保证你总是读取最新的数据,参见https://www.elastic.co/guide/en/elasticsearch/reference/current/near-real-time.html,所以无论哪种方式,即使只有一个节点,你也可能得到过时的数据。

我将忽略标签 [mongo 和 elastic] :)

您打算执行的操作称为 Dynamo 样式复制。该系统在设计上最终是一致的。 (我前一段时间读到它可以通过一些努力得到很强的一致性,但我不记得那篇论文是否正确。)

回到 dynamo 和 quorum:对于三个节点,您希望至少有 2 个节点来保存写入,以假设写入已成功。重要的一点是你需要两个节点来向客户报告成功,但数据仍然应该发送到三个节点。

让我们假设数据被写入两个节点,第三个失败,但稍后恢复在线。要读取数据,您还必须从任意两个节点读取数据。您将向所有三个发送读取请求,但只需要两个就可以向客户报告。这将为您提供法定人数:2+2>3。这保证了写入和读取之间存在交集。

当网络良好且节点健康时,这将正常工作。但是您会 运行 进入主要挑战者、丢失更新和冲突解决等等。但无论哪种方式,系统都不会基于设计本身的强一致性。

让我描述另一个有趣的问题来说明弱一致性:

  • 节点 1 获得写入
  • 其余过程失败;节点 1 有新数据,但节点 2 和 3 没有
  • 现在,当您读取时,在法定人数条件下,您可能会或可能不会看到节点 1 的值 - 因为您正在选择任何两个节点进行读取,所以节点 1 可能不在该集合中。

长话短说,dynamo 不利于强一致性,我们进入解决方案的 Raft 部分。

Raft 会满足您的需求。一致的系统。有一个问题需要注意。大多数示例都专注于编写 - raft 维护消息日志,并且共识用于就这些消息的顺序(和内容)达成一致。

但是当您进行读取时,您不能只转到一个节点,或任何两个节点,或三个节点并读取值。您还必须通过 Raft 进行读取,方法是将读取操作附加到 raft 的日志。这称为可线性化读取。

我会在这里停下来,因为这是一个非常复杂的主题(但并非不可能学习)。

希望这给了你足够的想法去探索。