<!-- title: 删不掉才不打架：CRDT靠墓碑实现协作 -->
<!-- domain: programming -->
<!-- video: https://www.youtube.com/watch?v=S7nsl5pYndg -->

# 删不掉才不打架：CRDT 靠"墓碑"实现协作

两个人同时改一个文档，为什么不会把文件改炸？这是协作软件每天都在默默回答、却极少有人深究的问题。YouTube 上 pixperk 频道的这期 **《CRDTs Explained: Building a Collaborative Whiteboard from Scratch》**，用 20 分钟从零手搓一个多人协作白板，把"协作不冲突"这件事从直觉拉到了数据结构层面。它的真正价值，不是教你画白板，而是揭示了一个反直觉的工程真相：**多人协作的根本解法，不是加锁、不是抢顺序、不是让服务器当裁判，而是让"删除"这件事变得不可能**。

这听起来荒谬，但正是这层荒谬，撑起了 Figma、Notion、Linear、Excalidraw、tldraw 这一整代协作产品的底层。今天这篇，我们就顺着这期视频，把 CRDT（Conflict-free Replicated Data Type，无冲突复制数据类型）的内核彻底拆开。

## 视频背景卡片

| 项目 | 内容 |
|------|------|
| 视频标题 | CRDTs Explained: Building a Collaborative Whiteboard from Scratch (System Design) |
| 频道 | pixperk（yashaswi mishra） |
| 时长 | 约 20 分钟 |
| 核心主题 | 从零构建协作白板，理解 CRDT 如何解决并发编辑冲突 |
| 适合人群 | 想搞懂 Figma/Notion 这类协作产品底层的中高级开发者 |

## 核心内容拆解

### 1. 协作的真正难题不是网络延迟，是"同时"

视频一上来就把问题摆正了。多数人以为协作软件的难点是"网络不稳""消息丢了"，其实那些都是工程问题，早有重传和 ACK 解决。真正的硬核难点是 **concurrency（并发）**——两个用户在通信往返的那段时间窗口里，各自基于旧状态做出了相互矛盾的决定。

举个视频里反复出现的典型场景：一块白板上有个圆形，A 把它拖到左上角，几乎同一时刻 B 把它拖到右下角。两条"移动"指令最终都到达服务器，到底听谁的？这不是 bug，这是分布式系统里 **同时性没有绝对定义** 的必然结果。爱因斯坦的相对论在工程世界里同样成立：在没有全局时钟的网络里，"谁先谁后"本身就无法判定。理解这一点，是理解后面一切的前提。

### 2. 老办法 OT——让服务器当唯一的裁判

在 CRDT 普及之前，整个行业的标准答案叫 **OT（Operational Transformation，操作变换）**，Google Docs 就是靠它跑了几十年。OT 的思路很直白：既然无法避免冲突，那就让一台中心服务器把所有人的操作排成一个唯一的全局顺序，再对"并发的操作"做数学变换，让它们相互兼容。

这套机制能跑，但代价巨大。第一，它**强依赖中心服务器**，服务器挂了协作就死，也无法真正离线工作。第二，OT 的变换函数出了名的难写对，业内有个梗叫"OT is hard"，多家大厂都栽过跟头。本质上，OT 是在用"抢一把全局锁"的思路解决冲突——只不过这把锁是一台服务器的时间序权威。视频把这条路当作对照组，是为了衬托 CRDT 的颠覆性。

### 3. CRDT 的反直觉内核：不抢顺序，而是让顺序无所谓

**（这是视频和整个 CRDT 理论的核心论点）** CRDT 直接掀桌子：既然抢顺序这么痛苦，那能不能让"顺序根本不重要"？

办法是设计数据结构时，让合并操作满足三条数学性质：**交换律**（a⊕b = b⊕a）、**结合律**、**幂等性**（a⊕a = a）。只要这三条成立，那么不管消息以什么顺序到达、到达几次、谁先谁后，所有副本最终都会收敛到同一个状态。这套东西在数学上叫 **join-semilattice（连接半格）**——你不需要懂格论，只需要记住一句话：**只要合并满足交换律，就不再需要裁判。**

这套理论在 2011 年由 Marc Shapiro、Nuno Preguiça、Carlos Baquero、Marek Zawirski 四人在论文里正式定义，最初动机正是协作文本编辑和移动端弱网场景。某百科的 CRDT 词条把它概括为三大特征：任意副本可独立并发更新、算法自动消解不一致、所有副本保证最终收敛。注意是"最终一致"，不是"强一致"——CRDT 牺牲了瞬时一致性，换来了去中心化和离线能力。

### 4. 删不掉才不打架——墓碑是 CRDT 的灵魂

**（标题的来源，也是视频里最容易被一笔带过的深层代价）** 要让所有操作都满足交换律和幂等性，CRDT 付出了一笔隐晦但巨大的代价：**你几乎不能真正"删除"任何东西**。

为什么？设想 A 在删除元素 X 的同时，B 正在往同一个集合里添加一个新元素 X。如果 A 的删除是"物理删除"，那它会把 B 即将到达的新 X 也一并抹掉，造成丢数据。为了在并发世界里保证安全，CRDT 的删除一律是 **软删除**——给元素盖个"墓碑"（tombstone），标记为已死，但数据本身永远留在结构里。

这就是 CRDT 最反直觉的地方：**协作越顺畅，垃圾越堆越多**。一份协作文档可能删得只剩三行字，但底层数据结构里却塞满了成千上万个墓碑。所以 CRDT 的"垃圾回收与压缩"（garbage collection / compaction）是一整片独立的研究领域，Yjs、Automerge 都在这上面花了大量功夫。理解了墓碑，你才真正理解 CRDT 不是"免费午餐"。

### 5. 一切从计数器和集合开始——CRDT 的积木

**（延伸：视频偏重白板实战，这里补全 CRDT 的理论积木）** CRDT 不是一种数据结构，而是一整族，靠几个原子积木拼出来：

- **G-Counter（只增计数器）**：每个副本维护一个向量，自己只加自己的那一格；合并时取逐项最大值。天然交换、幂等。
- **PN-Counter（正负计数器）**：两个 G-Counter，一个记增加、一个记减少，最终值 = 增量之和 − 减量之和。点赞数、库存数都靠它。
- **G-Set（只增集合）**：合并 = 并集。
- **2P-Set（两段式集合）**：一个加集 + 一个删集（墓碑），元素"在加集且不在删集"才算存在。
- **OR-Set（Observed-Remove Set）**：每次添加都带一个唯一标签，删除只干掉自己"见过"的那些标签——巧妙解决了"加了又删又加"的经典冲突。
- **LWW-Register / LWW-Map（Last-Write-Wins）**：每个字段配时间戳，后来的覆盖先来的。**这一块，正是协作白板用来存图形位置的主力。**

这些积木本身就是值得单独成篇的知识宝藏，Redis、Riak、Azure Cosmos DB 这些分布式数据库都内置了 CRDT 数据类型。

### 6. 文本是最难的——序列 CRDT 与"位置编号"

**（延伸）** 白板好做，因为它本质是"一堆独立图形的集合"，天然低冲突。真正难的是**文本**。在一段文字里插入字符，本质是个**序列问题**：你按"第 5 个位置插入一个 H"，可此时另一个人也在第 5 个位置插了东西，下标立刻对不上，整段文字错位。

解决思路有两种：**分数索引**（Logoot、LSEQ）——把每个字符的位置编成一个有理数，永远插在两个邻居之间，靠无限细分避免冲突；或者 **RGA（Replicated Growable Array）**——用带全局唯一 ID 的链表，并发时的"平局"用一个确定性的决胜规则（比如副本 ID 大小）来打破。WOOT、Treedoc、LSEQ、RGA 这一串名字，就是过去十几年学界啃"序列 CRDT"这块硬骨头留下的脚印。正是因为文本这么难，才催生了 Yjs、Automerge 这类工业级通用库——它们封装好了这些坑，让普通开发者不用重新发明轮子。

### 7. 白板为什么是 CRDT 的甜区——pixperk 的实战落点

回到视频本身。pixperk 选择白板不是偶然，而是因为**白板是 CRDT 最舒服的落点**。一块白板 = 一组图形，每个图形 = 一个唯一 ID + 一组属性（x、y、宽高、颜色）。映射到 CRDT：

- 加图形 → 往 OR-Set 里 add；
- 移动图形 → 对 x、y 字段做 LWW 更新（时间戳新的赢）；
- 删图形 → 打墓碑。

因为每个图形用不可变的唯一 ID 寻址，根本不存在"下标错位"那种灾难性冲突。两个人改的是不同图形？完美交换律，互不干扰。改的是同一个图形的同一属性？LWW 谁晚谁赢，干净利落。这正是 Figma、tldraw、Excalidraw 不约而同收敛到的模型。视频用从零撸代码的方式，让你亲手把这个映射走一遍，比读十篇博客都通透。**（Figma 工程团队自己的博客《How Figma's multiplayer technology works》也佐证：他们对属性级冲突采用 last-writer-wins 思路，整体精神与 CRDT 高度一致。）**

### 8. 从"在线协作"到"本地优先"——CRDT 的真正革命

**（延伸：视频的格局升华）** 顺着视频往远处看，CRDT 最大的红利其实不是"看到别人的光标实时动"，而是 **Local-First Software（本地优先软件）**。

因为 CRDT 的合并不依赖顺序、不依赖中心服务器，所以你可以**完全离线**改一份文档，三天后联网，它照样能和别人改的版本正确合并、收敛一致。这颠覆了过去二十年"服务器是唯一真相、客户端只是个笨视图"的架构。Martin Kleppmann（《Designing Data-Intensive Applications》作者）2019 年那篇 "Local-first software" 把这定义为一场革命，而 Automerge、Yjs 就是这场革命的施工队——Linear 的同步引擎、Apple Notes、Notion 的离线能力，背后都是同一套逻辑。

## 知识结构图

```mermaid
flowchart TD
    P([并发编辑难题<br/>两人同时改同一段])
    P --> OT[OT 操作变换<br/>中心服务器排全局顺序<br/>强依赖中心 / 变换函数难写]
    P --> CR[CRDT 无冲突复制数据类型<br/>不抢顺序 让顺序无所谓]

    CR --> MATH{数学内核<br/>合并满足 交换律+结合律+幂等性<br/>= Join-Semilattice 连接半格}
    MATH --> COST[代价: 墓碑 Tombstone<br/>删不掉 只能软删除 → 需 GC 压缩]

    CR --> BLOCKS[CRDT 积木族]
    BLOCKS --> B1[G-Counter / PN-Counter<br/>计数]
    BLOCKS --> B2[G-Set / 2P-Set / OR-Set<br/>集合]
    BLOCKS --> B3[LWW-Register/Map<br/>属性级 last-writer-wins]
    BLOCKS --> B4[序列 CRDT<br/>RGA / LSEQ / Logoot<br/>文本插入最难]

    B3 --> WB[协作白板甜区<br/>图形=唯一ID+LWW属性<br/>Figma/tldraw/Excalidraw]
    B4 --> LIB[工业级库<br/>Yjs / Automerge]
    WB --> LF[Local-First 本地优先<br/>离线可改 联网收敛]
    LIB --> LF
```

## 实战启示

对开发者而言，这期视频和 CRDT 这套体系能立刻转化为几条行动指南。

**第一，别再自己造协作的轮子。** 任何要做实时协作、多端同步、离线编辑的产品，第一反应应该是上 Yjs 或 Automerge，而不是手写 OT。Yjs 在前端协作（富文本、白板、表格）几乎是事实标准，Automerge 在 local-first 和移动端弱网更胜一筹。OT 的变换函数坑深似海，自己写大概率会埋下"偶发性丢字"的定时炸弹。

**第二，用 CRDT 友好的方式设计数据。** 核心原则：**一切共享状态都用不可变的唯一 ID 寻址，属性按 LWW 更新，删除一律打墓碑。** 永远避免"按下标原地修改共享数组"这种反 CRDT 的写法——那是冲突的温床。哪怕你不做协作产品，这套"不可变 ID + 幂等合并"的心智模型，对任何分布式系统、异步任务、事件溯源、幂等 API 设计都直接受益。

**第三，提前规划墓碑的生命周期。** CRDT 文档只增不减（逻辑上），长期协作的产品必须设计 compaction 机制——什么时候能安全回收墓碑、如何做快照压缩。这是 Yjs/Automerge 文档里专门强调、却最容易被新手忽略的部分。

**第四，搞清楚你要的是 LWW 还是序列 CRDT。** 如果你做的是白板、表单、看板这类"独立对象"场景，LWW-Map 足矣，简单高效；如果做的是富文本/代码这类"序列"场景，老老实实用序列 CRDT，别幻想用下标能搞定。选错积木，后面全是 bug。

## 延伸学习路径

1. **Martin Kleppmann 的两场经典演讲**——"Conflict-free Replicated Data Types"（讲透 CRDT 数学）与 "Local-first software"（讲透愿景），B 站/YouTube 都有字幕版，是理解这领域的最佳入口。
2. **2011 年原始论文**——Shapiro 等人《A comprehensive study of Convergent and Commutative Replicated Data Types》，HAL/INRIA 开放获取，理论地基，适合想啃硬核的读者。
3. **Yjs 官方文档（yjs.dev，作者 Kevin Jahns）**——工业级 CRDT 的最佳实践与 API，看完就能上手。
4. **Automerge 文档 + Kleppmann《Designing Data-Intensive Applications》第 5/9 章**——前者是 local-first 工具链，后者是分布式数据系统的圣经级教材。
5. **Figma 工程博客《How Figma's multiplayer technology works》**——一线大厂如何把这套理论落地到千万级用户的真实产品，配合 tldraw、Excalidraw 的同步机制文章一起看，实战味最浓。

## 一句话总结

CRDT 教会我们的最大洞见是：**解决冲突最高明的办法，不是更强力地协调，而是把数据结构设计得"冲突根本无从发生"——代价是，你永远删不掉任何东西。**

## 参考来源

1. [CRDTs Explained: Building a Collaborative Whiteboard from Scratch — pixperk](https://www.youtube.com/watch?v=S7nsl5pYndg) — 本文分析的核心视频
3. [How CRDTs make multiplayer text editing part of Zed's DNA — Zed Blog](https://zed.dev/blog/crdts) — 序列 CRDT 在代码编辑器中的工程实践
4. [Understanding CRDTs: The Magic Behind Collaborative Editing — singhkunal2050](https://singhkunal2050.dev/blog/understanding-crdts-the-magic-behind-collaborative-editing/) — CRDT 直观讲解补充
5. [How Figma's multiplayer technology works — Figma Engineering Blog](https://www.figma.com/blog/how-figmas-multiplayer-technology-works/) — 一线协作产品的冲突处理实战
> 本文由「51学通信」技术团队出品。51学通信由通信行业18年老兵**爱卫生**（微信ID: gprshome201101）维护，聚焦5G/6G核心网、3GPP标准解读、通信前沿技术深度分析。
> 出版作品有《5G核心网原理与实践》《IMS原理与实践》等4本通信专业书籍。知识星球「51学通信」提供深度技术答疑和行业洞察。
### 完成摘要

### 下一步

- Round 13 对应 **business（商业与创业）**，建议下一轮提前规划一个未覆盖的商业角度（如订阅经济、定价心理学、平台双边市场等）
- 如需，可把本文转成 51学通信风格 PPT 或短视频脚本（已有对应 skill）

### 注意事项

- 因网络搜索受限，视频播放量为估算口径（频道为系统设计领域活跃创作者），如需精确数据可在搜索额度恢复后补全
- 文中"延伸"部分为基于 CRDT 公开理论的技术补全，非视频原话，已显式标注

> [!tip] 51学通信知识星球
> **星主：爱卫生**（微信：gprshome201101）——公众号「51学通信」创办者，19 年通信行业运维与讲师经验，著有《GPRS网络信令实例详解》《EPC原理与实践》《5G核心网原理与实践》三本纸质书（各大电商平台有售）。
> **星球定位**：深耕 5G 核心网、IMS、NFV/AI、云原生等 ICT 领域，主打运维岗位与信令分析。
> **当前规模**：5200 篇主题 / 990 个回答，198 元/年。
> **加入福利**：加入星球后请加爱卫生微信，拉您进知识星球年度陪伴与答疑群——不限量回复您的各种问题，另有群内分享。
> **AI 加持**：51学通信知识星球已作为 skill 接入 WorkBuddy，使用 deepseek-4-flash 在海量星球帖子中精准找贴、找视频、找 FAQ 和案例，做总结、找文档和 PPT 等场景全覆盖。

![51学通信知识星球海报-扫码加入](https://wiki.500nian.com/images/zhixingqiu_qrcode.png)
