我认为 CAP 定理对您的情况并没有真正的帮助。处理分区的分布式系统需要决定当一个部分想要对数据进行修改但无法到达另一部分时要做什么。一种解决方案是让写入等待——这是因为“分区”而放弃了“可用性”,这是 CAP 定理提出的选项之一。但是还有更多有用的选择。最有用(高度可用)的选项是允许两个部分独立编写,并在它们可以再次连接时调和冲突。问题是如何做到这一点,不同的分布式系统选择不同的方法。
一些系统,如 Cassandra 或 Amazon 的 DynamoDB,使用“最后一个写入者获胜”——当我们看到两个冲突写入之间发生冲突时,最后一个(根据一些同步时钟)获胜。为了使这种方法有意义,您需要非常小心地对数据建模(例如,注意冲突解决导致两种状态的无效混合的情况)。
在其他系统中(以及在 Cassandra 和 DynamoDB 中——在它们的“集合”类型中),写入仍然可以在不同的节点上独立发生,但有更复杂的冲突解决方案。一个很好的例子是 Cassandra 的“列表”:可以发送一个更新说“将项目 X 添加到列表”,另一个更新说“将项目 Y 添加到列表”。如果这些更新发生在不同的分区上,则稍后通过将X和 Y 添加到列表中来解决冲突。诸如此列表之类的数据结构 - 允许在两个节点上以某些方式独立修改内容,然后以合理的方式自动协调,称为无冲突复制数据类型(CRDT)。
最后,在亚马逊的 Dynamo 论文中使用了另一种方法(不要被他们当前的 DynamoDB 服务混淆!),称为“矢量时钟”:当您想要写入一个对象时 - 例如,购物车 - 您首先读取当前对象的状态并获得一个“矢量时钟”,您可以将其视为您获得的数据的“版本”。然后您进行修改(例如,将商品添加到购物车),并写回新版本,同时说明您开始使用的旧版本是什么。如果其中两个修改同时发生在不同的分区上,我们稍后需要协调这两个更新。矢量时钟允许系统确定一个修改是否比另一个修改“更新”(在这种情况下不存在冲突),或者它们确实存在冲突。当他们这样做时,特定于应用程序的逻辑用于调和冲突。在购物车示例中,如果我们看到冲突是在一个分区中将商品 A 添加到购物车中,而在另一个分区中,将商品 B 添加到购物车中,那么直接的解决方案是只添加时间 A 和B 到购物车。
您可能应该选择其中一种方法。只是说“CAP 定理不允许我这样做”通常不是一种选择;-) 事实上,在某些方面,您面临的问题是不同的比我提到的一些系统。在这些系统中,常见情况是每个节点始终连接(无分区),延迟非常低,他们希望这种常见情况快速。在您的情况下,您可能可以假设相反:这两个部分通常不连接,或者如果它们连接存在高延迟,因此冲突解决是常态,而不是例外。因此,您需要决定如何解决冲突 - 如果一个人在一台设备上添加一个会议并在另一台设备上添加一个不同的会议会发生什么(很可能,只是将两者都保留为两个会议......),您怎么知道一台设备修改了一个预先存在的会议并且没有添加第二个会议(矢量时钟?唯一的会议 ID?等)所以冲突解决最终修复了现有的会议而不是添加了第二个会议?等等。
您还需要考虑另一个问题。我们什么时候进行这些和解?在我上面列出的许多系统中,协调发生在读取时:如果客户端想要读取数据并且我们突然在两个可达节点上看到两个冲突的版本,我们会协调。在您的日历应用程序中,您需要一种稍微不同的方法:客户端可能只会在未连接时尝试读取(使用)日历。您需要利用他连接时的难得机会来调和所有差异。此外,您可能需要“推送”更改 - 例如,如果服务器上的数据发生更改,则可能需要告诉客户端,“嘿,我有一些更改的数据,来协调”,因此最终用户会立即查看有关新会议的通知,例如远程添加的通知(例如,可能由共享同一日历的不同用户执行)。你需要弄清楚你想如何做到这一点。同样,没有像“使用 Cassandra”这样的神奇解决方案。