CRDT(无冲突复制数据类型)详解

CRDT 全称:

Conflict-free Replicated Data Type

它是一类专门用于分布式系统数据同步的数据结构。

核心目标:

让多个副本(Replica)在没有中心协调、甚至同时修改数据的情况下,最终能够自动合并,并保证所有副本最终一致。

它主要解决:

  • 多地编辑数据同步

  • 分布式缓存同步

  • 离线应用数据合并

  • 多人协作文档

  • 分布式数据库复制

  • Edge Computing 数据同步


1. 为什么需要 CRDT?

先看一个现实问题。

假设有一个文档:

title = "hello"

有两个用户:

        Replica A          Replica B

          hello             hellor
             |                 |
             |                 |
       修改为 helloA     修改为 helloB

结果:

A:

helloA


B:

helloB

现在两个副本同步。

问题:

最终应该是什么?

可能:

helloA

也可能:

helloB

也可能:

helloAB

传统做法:

方案1:加锁

例如:

用户A获取锁

修改

释放锁


用户B修改

问题:

  • 延迟增加

  • 网络分区时不可用

  • 分布式锁复杂


方案2:中心服务器仲裁

例如:

Client A
    |
    |
 Server
    |
    |
Client B

服务器决定:

谁覆盖谁

问题:

  • 中心节点压力

  • 离线无法工作


CRDT 的思想:

不需要决定谁赢,而是设计一种数据结构,让所有修改天然可以合并。


2. CRDT 的核心思想

普通数据:

value

例如:

count = 10

CRDT:

value + metadata

例如:

{
 value:10,

 version:{
    A:5,
    B:3
 }
}

它保存:

  • 数据

  • 修改历史信息

  • 因果关系

这样:

多个副本合并时:

A的数据

+

B的数据

↓

自动计算结果

3. CRDT 的两个核心性质

CRDT 能保证最终一致,依赖数学性质。

1. 交换律(Commutative)

顺序无关:

A + B

=

B + A

例如:

增加:

+1

+2

无论:

+1 后 +2

还是:

+2 后 +1

结果:

3

2. 结合律(Associative)

组合顺序无关:

(A+B)+C

=

A+(B+C)

3. 幂等律(Idempotent)

重复执行不会影响:

A+A=A

例如:

同步消息:

update(x)

发送两次:

update(x)
update(x)

结果一样。


这三个性质保证:

即使:

  • 网络乱序

  • 消息重复

  • 延迟不同

最终仍然一致。


4. CRDT 分类

CRDT 主要分两类:

             CRDT

              |
      -----------------
      |               |
   State-based     Operation-based

   状态型            操作型

5. State-based CRDT(状态型)

也叫:

CvRDT(Convergent Replicated Data Type)

思想:

副本之间交换完整状态,然后合并。

例如:

Replica A

{
 count:5
}


Replica B

{
 count:8
}

同步:

merge(A,B)

得到:

count=?

关键:

merge 必须满足:

交换律

结合律

幂等律

6. 最简单例子:G-Counter

G-Counter:

Grow-only Counter,只增加计数器

场景:

点赞数量。

三个节点:

A
B
C

每个节点维护:

{
 A:0,
 B:0,
 C:0
}

用户在 A 点赞:

A:

{
 A:1,
 B:0,
 C:0
}

用户在 B 点赞:

B:

{
 A:0,
 B:1,
 C:0
}

同步:

合并:

取每个节点最大值


{
 A:max(1,0),
 B:max(0,1),
 C:0
}

=

{
 A:1,
 B:1,
 C:0
}

结果:

count=2

为什么不会冲突?

因为:

max()

满足:

max(a,b)=max(b,a)

7. PN-Counter(可增减计数器)

G-Counter 只能增加。

现实:

点赞:

+1

取消点赞:

-1

怎么办?

PN-Counter:

拆成两个:

P Counter

增加


N Counter

减少

例如:

用户点赞:

P:

A=10

取消:

N:

A=2

最终:

value=P-N

=10-2

=8

8. Set 类型 CRDT

集合:

users

两个副本:

A:

{
Tom
}

B:

{
Jerry
}

同步:

union

结果:

{
Tom,
Jerry
}

但是删除怎么办?

例如:

A:

删除 Tom

B:

添加 Tom

冲突:

到底有没有 Tom?

所以出现:

OR-Set

Observed Remove Set

它记录:

元素

+

唯一标识

例如:

添加:

Tom#001

删除:

删除 Tom#001

如果:

B:

Tom#002

那么:

仍然存在。


9. 最经典应用:协同编辑

例如:

Google Docs。

两个用户:

User A:

hello


User B:

hello

同时:

A:

插入:

hello A

B:

插入:

hello B

最终:

hello AB

或者:

hello BA

关键:

所有人看到一致结果。


这类 CRDT:

例如:

  • Yjs

  • Automerge

采用:

Sequence CRDT

专门解决:

文本序列编辑。


10. Sequence CRDT 怎么实现文本?

普通字符串:

hello

如果插入:

hello

       ^
       插入 X

问题:

字符位置会变化。

CRDT 不保存:

index=5

而保存:

字符ID

例如:

h
 id=001

e
 id=002

l
 id=003

插入:

x

after id=003

于是:

h
e
l
x
l
o

即使:

两个用户同时插入:

after id=003

也可以根据:

  • 时间戳

  • 节点ID

排序。


11. CRDT 和 Paxos/Raft 的区别

很多人会混淆。

Raft

目标:

强一致

流程:

Client

 ↓

Leader

 ↓

Followers

特点:

  • 有 Leader

  • 需要多数派

  • 写入需要协调

例如:

数据库。


CRDT

目标:

最终一致

流程:

Replica A

      ↔

Replica B

      ↔

Replica C

特点:

  • 无中心

  • 可离线

  • 自动合并

例如:

协作文档。


对比:

Raft CRDT
一致性 强一致 最终一致
中心节点 需要 不需要
网络分区 牺牲可用性 保持可用
冲突处理 拒绝冲突 自动合并
适合 数据库 协作系统

12. CRDT 的优点

1. 无需锁

多个节点:

同时写

也可以。


2. 支持离线

例如:

手机:

飞机模式

编辑文档

回来:

同步

自动合并

3. 高可用

网络断开:

Replica A

继续工作


Replica B

继续工作

13. CRDT 的缺点

CRDT 不是万能。

1. 数据膨胀

因为需要保存:

版本

ID

操作历史

例如:

文本:

hello

实际:

字符

+

UUID

+

时间戳

+

关系信息

空间明显增加。


2. 复杂度高

简单:

value=10

变成:

value

+

metadata

+

merge algorithm

3. 不适合强事务

例如:

银行转账:

A账户 -100

B账户 +100

不能接受:

最终一致

需要:

Raft/Paxos/事务。


14. 实际应用案例

在线文档

  • Google Docs

  • Notion

  • Figma

数据同步

  • Redis CRDT

  • Riak DT

分布式数据库

  • AntidoteDB

前端协作框架

  • Yjs

  • Automerge


15. 一句话总结

CRDT 是一种通过设计数据结构,使分布式副本之间的修改天然可合并的数据类型,它利用数学上的交换律、结合律、幂等律,让多个节点无需加锁和中心协调,也能最终达到一致。

简单理解:

传统分布式:

多个地方修改数据
        |
        ↓
必须有人裁决冲突


CRDT:

多个地方修改数据
        |
        ↓
数据结构自己知道如何合并
        |
        ↓
最终一致

如果你前面关注的是缓存、一致性 Hash、分布式系统设计,那么 CRDT 可以进一步理解为:

一致性 Hash 解决「数据放哪里」;CRDT 解决「多个地方同时改同一份数据,如何自动合并」。

二者是分布式系统中两个不同维度的问题。