CRDT——Conflict-free replicated data type

CRDT 是 "Conflict-free Replicated Data Types" 的缩写，中文可以翻译为 "无冲突复制数据类型"。这是一种可以在多个设备或节点间复制和同步的数据结构，而不需要进行复杂的冲突解决。CRDTs 被设计用来解决分布式系统中的一些困难问题，特别是在可能出现网络分区或延迟的情况下。

先来想象一下一个常见的问题：多人在线协作编辑同一份文档（比如 Google Docs）。每个人都可以在自己的设备上进行编辑，然后这些更改需要同步到其他所有人的设备上。但是，如果两个人几乎同时在同一个位置进行了编辑，我们应该怎么处理呢？这就是一个典型的冲突问题。

传统的解决方式可能是使用某种锁机制或者冲突解决策略，但是这些方法往往会使系统变得复杂，且在网络延迟或断开的情况下表现不佳。

CRDTs 提供了一种更优雅的解决方案。CRDTs 的设计思路是，如果我们能设计出一种数据结构，使得对其的所有可能操作都是可交换的（也就是说，不管操作的顺序如何，结果都是一样的），那么我们就可以避免冲突。这样，每个设备都可以自由地、无需等待或锁定地进行操作，然后将这些操作同步到其他设备。只要所有的操作最终都能到达所有的设备，那么所有设备上的数据状态就会一致。

CRDTs 是一种数据结构，用于简化分布式数据存储系统和多用户应用。在许多系统中，某些数据的副本需要存储在多台计算机（即副本）上。这类系统的例子包括：

存储数据在本地设备上并需要将该数据同步到同一用户的其他设备上的移动应用（例如日历，笔记，联系人或提醒）；

分布式数据库，这些数据库在同一数据中心或不同位置维护数据的多个副本，以便在一些副本离线时系统能够继续正常工作；

在线协同软件，如 Google Docs, Trello, Figma 等，多个用户可以同时对同一文件或数据进行更改；

大规模数据存储和处理系统，这些系统复制数据以实现全球范围内的可伸缩性。

所有这些系统都需要处理数据可能在不同副本上同时被修改的情况。大体上，处理此类数据修改的方式有两种：

强一致性复制
副本彼此协调以决定何时以及如何应用修改。这种方法支持如可串行化事务和线性化等强一致性模型。然而，等待这种协调会降低这些系统的性能；此外，CAP 定理告诉我们，当一个副本从系统的其余部分断开时（例如由于网络分区，或者因为它是一个具有间歇性连接性的移动设备），无法对该副本进行任何数据更改。

乐观复制
用户可以独立于其他副本修改任何副本上的数据，即使该副本处于离线或与其他副本断开连接状态。这种方法实现了最大的性能和可用性，但当多个客户端或用户同时修改同一份数据时可能会导致冲突。然后需要在副本之间通信时解决这些冲突。

CRDTs 用于乐观复制系统中，负责解决冲突。 CRDTs 确保无论在不同副本上进行了什么数据修改，数据始终可以合并为一致的状态。这个合并由 CRDT 自动执行，不需要任何特殊的冲突解决代码或用户干预。

此外，CRDTs 的一个重要特性是支持去中心化运作：它们并不假定使用单一服务器，因此可以在对等网络和其他去中心化环境中使用。在这方面，CRDTs 与 Google Docs、Trello 和 Figma 使用的算法不同，这些工具要求用户之间的所有通信都通过服务器进行。

为了实现这一点，CRDT 需要对数据的修改进行一些特殊的处理。比如，对于一个 CRDT 集合，添加和删除操作可能需要记住一些额外的信息，以便在其他设备进行同步时可以正确地处理冲突。其中一个常见的类型，即 “Grow-only Set（只增不减集合）”。在这个类型的 CRDT 中，你可以添加元素到集合中，但不能从集合中移除元素。这个设计的好处是，添加操作是可交换的，即不论你按照何种顺序添加元素，最后的结果都是相同的。

但是，现实生活中我们常常需要从集合中移除元素。为了解决这个问题，另一种 CRDT，叫做 “Two-phase Set（两阶段集合）”。在这个类型的 CRDT 中，每一个元素都有两个状态：添加和删除。初始状态为添加，当你删除一个元素时，其状态转为删除。这样，即使有其他设备同时添加同一元素，只要有一个设备标记了该元素为删除，该元素就会被视为已删除。这也就是说，删除操作在这个集合中具有优先级。这样的设计使得在不同设备之间同步时能够正确地处理冲突。

总的来说，CRDTs 的设计都是以这样的方式来处理数据修改的，即通过一些特殊的处理，使得操作可以在不同的设备上并行进行，并且可以在合并时保持数据的一致性。这样的设计使得 CRDTs 非常适合于分布式系统和多用户应用，因为它们可以处理并行操作和网络延迟带来的复杂问题。

CRDT 有两种类型，都可以提供强最终一致性：基于操作的 CRDT 和基于状态的 CRDT。

基于状态的 CRDT 通常更容易设计和实现；它们对通信底层的唯一要求是某种 gossip protocol。它们的缺点是，每个 CRDT 的整个状态最终都必须传输给其他每个副本，这可能开销很大。相比之下，基于操作的 CRDT 只传输更新操作，通常很小。然而，基于操作的 CRDT 需要通信中间件的保证；在传输给其他副本时，操作不会被丢弃或重复，而且它们是按因果顺序传输的。

CmRDT
基于操作的 CRDT 也被称为交换性复制数据类型（commutative replicated data types）。CmRDT 副本只通过传输更新操作来传播状态。例如，单一整数的 CmRDT 可以广播操作（+10）或（-20）。副本接收更新并在本地应用这些更新。这些操作是可交换的。然而，它们不一定幂等。因此，通信基础设施必须确保一个副本上的所有操作都被传递给其他副本，没有重复，但以任何顺序传输都是可以的。

CvRDT
基于状态的 CRDT 被称为收敛复制数据类型（convergent replicated data types）。与 CmRDT 相反，CvRDT 将其完整的本地状态发送给其他副本，在这些副本中，状态必须通过可交换的、可结合的并且是幂等的函数合并。合并函数为任何一对副本的状态提供连接，因此所有状态的集合形成一个半格。更新函数必须按照与半格相同的偏序规则，单调地增加内部状态。函数必须内部状态，根据与半网格相同的偏序规则。