← 返回新闻中心
技术解读2025年11月21日·13 分钟

CRDT 不是银弹:Nexus 状态同步的工程取舍

为什么我们最终魔改了 CRDT,而不是直接用现成实现

林思羽

亚诺尔科技 · 协议组负责人

CRDT(Conflict-free Replicated Data Type)经常被以一种过于美好的方式介绍:节点之间无需协调,并发修改自动收敛到一致状态。这个概括既准确又误导——它对了语义,错了代价。

CRDT 的真实代价

在 Nexus 早期版本里,我们用过 Yjs 风格的 CRDT。用了三个月之后,我们发现以下几件事都不像宣传里那样美好。

代价 1:元数据无限增长

原生 CRDT 为了保留并发修改的因果信息,会保存大量元数据(向量时钟、tombstone、操作历史)。在长寿命的 Agent 集群里,这些元数据会随时间无限增长。我们看到过单个状态对象的元数据膨胀到正文的 40 倍。

代价 2:合并复杂度

"自动收敛"这个承诺隐藏了 CPU 代价。在千 Agent 规模下,合并操作变成了显著的性能热点——某些节点 30% 的 CPU 都花在了合并上。

代价 3:调试灾难

CRDT 的合并语义不直观。当业务方看到状态收敛到一个"看起来不对"的值时,他们没办法用直觉解释为什么。我们需要花大量时间在工程师之间科普"这就是 CRDT 的合并规则"——这些时间本可以用来做更有价值的事。

Nexus 的非传统选择

我们最终在 2.0 版本里魔改了状态层,做了三个不太"教科书"的决定。

决定 1:因果 epoch + 增量快照,替代向量时钟

我们用一个粗粒度的因果 epoch(基于物理时钟 + 序列号)替代了细粒度向量时钟。代价是某些极端并发场景下需要更多的协商;收益是元数据从 O(n) 降到 O(1),集群运行 30 天后状态对象大小不再膨胀。

决定 2:不是所有数据都用 CRDT

原生 CRDT 思维倾向于把"所有共享状态"都做成 CRDT。但实际上很多数据(比如 Agent 的能力声明、协议版本、配置)变更频率极低、并发冲突几乎不可能发生。这些数据用经典的 leader-based 复制更合适。Nexus 现在是 70% leader-based + 30% CRDT 的混合状态层。

决定 3:合并语义对业务可见

我们让业务代码可以查询"这个值是怎么合并出来的"——不是泄漏 CRDT 内部细节,而是提供一个抽象的"合并轨迹"。这样当业务方对收敛结果有疑问时,他们能自己看到原因,不需要找协议组工程师。

工程信念:当一个学术抽象在生产中无法被业务方理解时,工程上的正确做法不是要求业务方学习这个抽象,而是给抽象增加可解释性。

收益

上面三个决定加起来,让 Nexus 的状态层在千 Agent 规模下:内存占用下降 71%、合并 CPU 占用下降 64%、跨团队的状态相关 issue 数量下降了 80%。

我们没有发明新的 CRDT,我们只是更诚实地承认它的代价,并在合适的地方拒绝用它。

TagsCRDTNexus协议设计