1

我最近从 cockroachdb 博客中读到了这篇很棒的文章,其中讨论了它们如何以类似于扳手但没有原子钟的方式保持一致性。这是帖子的相关部分:

CockroachDB 启动事务时,它会根据当前节点的挂墙时间选择一个临时提交时间戳。它还通过添加集群的最大时钟偏移[提交时间戳,提交时间戳+最大时钟偏移]来建立所选挂钟时间的上限。该时间间隔代表不确定性窗口。

...

只有当观察到一个值在不确定区间内时,CockroachDB 特定的机制才会启动。这里的核心问题是,鉴于时钟偏移,我们无法确定遇到的值是否在我们的事务开始之前提交。在这种情况下,我们只需执行不确定性重启,将临时提交时间戳刚好高于遇到的时间戳即可。至关重要的是,不确定性区间的上限在重启时不会改变,因此不确定性窗口会缩小。从许多节点读取不断更新的数据的事务可能会被迫多次重启,但永远不会超过不确定间隔,每个节点也不会超过一次。

具体来说,我不明白为什么不确定性窗口的上限在不确定性重新启动期间也不必被碰撞。这是一个例子来说明我的困惑:

假设我们有两个写入 A 和 B(在不同的节点上)。写入 A 的时间戳为 3,B 的时间戳为 5。假设最大时钟偏移量为 3 个时间单位,如果我们在时钟当前读取为 1 的节点上启动事务,我们将构建 [1, 4] 的不确定性窗口。当我们遇到 write A 时,我们将执行不确定性重新启动以包含它并将不确定性窗口减小到 (3, 4]。当我们遇到 write B 时,我们将忽略它,因为它位于不确定性窗口的上限之上。但是,由于我们的最大时钟偏移量是 3 个单位,并且 A 和 B 的时间戳相距不到三个单位,B 可能发生在 A 之前。但是我们在事务中包含了 A 而不是 B,所以我们没有一致性。

提前感谢您指出我所缺少的!

4

1 回答 1

0

好问题。这很微妙;我会试着解释发生了什么。

首先让我们看一下所涉及的交易。我们有两个写事务,A (ts=3) 和 B (ts=5)。如果这两个事务接触的键有任何重叠,它们将无法“乱序”提交,我们可以确定事务 A 发生在事务 B 之前。但是如果没有重叠的键,那么就有除了时钟之外,没有任何协调点可以保证这个顺序,并且由于时间戳足够接近,因此哪个“首先发生”是模棱两可的。

在存在这种歧义的情况下,我们想做的是分配某种顺序,以认为交易按照其时间戳所暗示的顺序发生。只要我们知道没有人观察到写入 B 而不是 A 的状态,这样做是安全的。这是由 CockroachDB 内部管理读/写交互的机制强制执行的(主要是名称不佳的TimestampCache)。如果事务 B 之后是时间戳 6 的事务 C,它读取密钥 A 和 B,则事务 A 将不再被允许在时间戳 3 提交(它将被推送到时间戳 7)。

只要数据库可以看到读取和写入之间的关系,这就会起作用。有时我们看不到这一点,如果写 B 对 A 有一些带外因果依赖,但从来没有一个事务同时触及两个键。这就是我们称之为“因果反转”的异常,是 CockroachDB 的可序列化隔离和 Spanner 的可线性化(或严格可序列化)隔离级别之间的差异。

更新不确定间隔的上限是避免因果反向异常的一种方法,尽管它会具有严重的实际缺点 - 您可能必须一遍又一遍地重新启动,直到您发现写入之间至少有最大时钟偏移的间隙在您的交易涉及的所有键上。这可能永远不会成功。

于 2020-06-14T14:57:45.253 回答