文档核心概念什么是 CRDT

什么是 CRDT?

CRDT(Conflict-free Replicated Data Type,冲突自由复制数据类型)是一种可以在网络中复制到多台计算机上的数据结构,各个副本能够独立并行更新,无需副本之间协调,同时保证不会出现冲突。

CRDT 常用于协作类软件,例如多名用户需要共同编辑或阅读的共享文档、数据库或状态。它同样可以应用于数据库软件、文本编辑器、聊天工具等场景。

CRDT 解决了什么问题?

以多人同时在线编辑同一份文档为例。

这一场景要求每位用户即便在不同用户并发修改(例如两个人同时修改标题)之后,也能看到相同的内容,这被称为一致性。(严格来说,CRDT 满足的是最终一致性,详见下文。)

即使用户离线,也能继续使用 CRDT;当网络恢复时,副本可以重新同步。它还支持通过 P2P 与其他用户协同编辑,这体现为分区容错性。因此 CRDT 非常适合构建去中心化应用:即便没有中心化服务器也能完成同步。

CRDT 的出现

Marc Shapiro 在 2011 年的论文《Conflict-Free Replicated Data Types》中首次提出 CRDT(Conflict-free Replicated Data Types,冲突自由复制数据类型)的正式概念。不过可以认为更早之前就已经奠定了基础,例如 2006 年的研究《Woot: An Algorithm for Collaborative Real-time Editing》。开发 CRDT 的主要动机,是为了解决在最终一致性模型下设计冲突解决机制的难题。CRDT 推出之前,相关文献给出的指导非常有限,临时拼凑的方案也容易出错。因此 Shapiro 的论文提供了一种简单且理论完备的方法,利用 CRDT 来实现最终一致性。

(注:Marc Shapiro 在 2007 年曾发表论文《Designing a commutative replicated data type》。2011 年他将 commutative 改写为 conflict-free,并将该定义扩展到了状态型 CRDT。)

根据 CAP 定理,分布式系统不可能同时完美满足以下三个条件。

  • 一致性(Consistency):每次读取要么返回最近一次写入的结果,要么返回错误;系统表现得像在访问同一份数据。
  • 可用性(Availability):每个请求都会得到非错误响应,但不保证返回的是最新的数据。
  • 分区容错性(Partition tolerance):即使系统不同组件之间的通信丢失或延迟导致网络分区,整个分布式系统仍能正常工作。

如果系统在限定时间内无法达成数据一致,就意味着出现了分区,只能在当前操作中在 C 与 A 之间做出选择,因此“完美一致性”会和“完美可用性”发生冲突。

CRDT 并不追求“完美一致性”,而是提供强最终一致性(Strong Eventual Consistency,简称 SEC)。这意味着站点 A 可能不会立即反映站点 B 的状态变化,但当 A 和 B 同步消息后,就能重新达成一致,并且无需再处理潜在冲突(CRDT 在数学上杜绝了冲突)。强最终一致性可用性 以及 分区容错性 并不矛盾,CRDT 在 CAP 上给出了一个良好的折中。

CAP 示意图 CRDT 满足 A + P + Eventual Consistency,是 CAP 下的优雅折衷

(注:CAP 定理作者 Eric Brewer 在 2012 年撰写了《CAP Twelve Years Later: How the “Rules” Have Changed》,指出“CAP 三选二”的说法其实容易误导。CAP 真正排除的只是极小设计空间内“完美可用性 + 完美一致性”的组合,即在存在分区时;实际上,针对 C 与 A 的权衡仍有很大自由度。CRDT 就是很好的例子。)

一个简单的 CRDT 示例

我们可以通过几个简单的例子了解 CRDT 如何实现强最终一致性

只增计数器(Grow-only Counter)

如何在分布式系统中统计事件发生次数,同时避免加锁?

G-Counter 动画
  • 让每个副本只递增自己的计数值 => 无需加锁同步,也不会产生冲突。
  • 每个副本同时保存其他副本的计数值。
  • 事件发生次数 = 所有副本计数值之和。
  • 副本只更新自身计数,不会与其他计数发生冲突,因此消息同步后即可保持一致。

只增集合(Grow-only Set)

G-Set 动画
  • 只增集合中的元素只能增加,不能删除。
  • 合并两个状态时,只需计算集合并集。
  • 因为只有增加操作,不会发生冲突,消息同步后即可保持一致。

这两种结构都是 CRDT,它们具备以下共同特性:

  • 副本之间可以独立并发更新,无需协调(加锁)。
  • 多次更新不存在冲突的可能。
  • 最终总能保证一致性。

原理简介

CRDT 分为基于操作(Op-based)和基于状态(State-based)两类,本文聚焦于基于操作的 CRDT。

操作型 CRDT 的原则是:如果两个用户执行完全相同的操作序列,文档的最终状态也必须完全一致。为此,每个用户都会保存自己对数据执行的所有操作(Operation),并将这些操作与其他用户同步,从而确保最终状态一致。该方法的关键挑战是保证操作顺序一致,尤其是在并行修改发生时。为了解决这一问题,操作型 CRDT 要求所有可能并行的操作都可交换,以满足最终一致性的要求。

CRDT 与 OT 的对比

CRDT 与 Operation Transformation(OT) 都可以应用在在线协作场景,两者之间的差异如下:

OTCRDT
OT 依赖中心化服务器协作;在分布式环境下很难实现CRDT 可以通过 P2P 等方式同步数据,天然支持分布式
最早的 OT 论文发表于 1989 年最早的 CRDT 论文发表于 2006 年
OT 算法为了保证一致性,设计更复杂CRDT 算法设计更简单即可保证一致性
更容易设计出能保留用户意图的 OT设计同时保留用户意图的 CRDT 算法更困难
OT 不会影响文档体积CRDT 文档比原始数据更大