许多分布式系统(例如数据库)表示它们可以提供强一致性。例如,假设数据的副本N
,要求W
节点确认写入,R
副本响应读取,Cassandra 文档说只要R + W > N
,你就会获得强一致性。直觉上,这是有道理的。但后来我开始在个人消息级别上考虑这个问题,我实际上无法理解它是如何实现的。
具体来说,假设我有一个复制因子为 3 的 Cassandra 集群。为简单起见,让我们假设只有一个数据分区,因此系统中正好有 3 个节点,A
、B
和C
个节点。客户端尝试写入一些数据,x = 11
,写入一致性为W = 3
,也就是说,仅当所有副本都确认写入时,写入才被视为完成。因此,客户端将写入请求发送给A
然后将其转发给B
并C
。让我们假设B
确认写入,但C
没有。然后写入应失败。然后,另一个客户端与R = 1
一起读取,并碰巧与B
交谈。R + W = 1 + 3 = 4 > 3
这应该是一个强一致性的阅读。但是,B
已经确认了写入,因此如果被询问,B
至少会在一段时间内返回x = 11
(它可能只是一个窗口,因为A
可能会告诉B
"没关系,写入失败")。如果客户端从不重试写入,我们现在向客户端提供了完全不正确的数据,并且似乎我们无法考虑这种强一致性。
我们可以开始考虑解决这个问题的方案。例如,也许协议是每个节点AC消息,但在A
再次联系它们并告诉它们提交(即两阶段提交)之前不会返回它。但是我们再次遇到麻烦,因为现在我们可以B
并且最初C
ACK,所以A
告诉他们提交,但C
未能收到该消息。因此,即使写入似乎已成功,从C
读取也无法返回x = 11
。尝试通过额外的消息传递轮次来解决此问题(例如,每个节点都必须确认提交阶段)也不可避免地会遇到问题,正如两个一般问题所证明的那样。
我在这里的推理显然有问题;如果使用得当,Cassandra确实提供了很强的一致性。我的问题是,在节点到节点协议级别,他们是如何做到的?
我认为这里的答案是这里的"强一致性"类似于未提交的读取,这意味着脏读,就像我最初的例子一样,实际上是允许的,并且确实发生了。事实上,我在Cassandra文档中发现了这一点:
如果其中一个节点上的写入失败,但在另一个节点上成功,Cassandra 将报告在该节点上复制写入失败。但是,在其他节点上成功的复制写入不会自动回滚。