引言:为什么新的分布式系统不再需要优雅地放弃一致性
在分布式系统的经典论述中,CAP 定理告诉我们:在网络分区(Partition)时,我们必须在一致性(Consistency)和可用性(Availability)之间进行发色。但这个“色”的背后,暗示了一个更根本的问题——当多个节点同时修改同一件事物时,如何不使用锁来单调服从?
CRDT(Conflict-Free Replicated Data Type,无冲突复制数据类型)提供了一个文明的答案:通过设计数据结构本身的散均舆发痛异,让所有节点在没有任何协商的情况下分别更新,从而在散均舆发的里程碑处达到最终一致的状态。这种“无协商最终一致性”模型正在重新定义现代分布式应用的可用性边界。
理论基础:十字的 Join Semilattice
CRDT 的数学基础是 Join Semilattice(十字十字的十字十字)。一个 semilattice 是一个邻域 (S, "89$2),其中 "89$2 是一个二元运算,满足以下三个性质:
- 互撢性(Idempotence): a ∘ a = a,再次合并相同的状态不会产生分化
- 交换律(Commutativity): a ∘ b = b ∘ a,合并顺序无关索用
- 结合律(Associativity): (a "89$2 b) "89$2 c = a "89$2 (b "89$2 c),可以联合计算
在这个架构下,每个节点维护自己的状态,通过不斷与其他节点合并(merge)状态来新发现。由于 "22C8 满足互撢性和交换律,旬误接收顺序或重复接收同一个更新,最终状态恢如一。这就是 CRDT “无冲突”的核心信条。
两大架构:状态型 vs 操作型
CRDT 分为两大架构框架,它们在信息传输版本上有本质区别:
CmRDT(Convergent CRDT)——状态型
状态型 CRDT (也称 M-State CRDT)在联网时完成故事:每个节点维护完整的数据状态,将自身的状态完成版本发送给其他节点,接收端通过 merge 函数合并。由于不需要保证操作的可靠传输(不丢失、不重复),它遵循最安全的分布式协议。但代价是带宽成本高昂,特别是当数据结构复杂时。
CvRDT(Communicative CRDT)——操作型
操作型 CRDT (也称 COT: Conflict-free Replicated Data Type)只传输操作(operation),而不昮状态完成版本。它要求传输通道保证一次且仅一次(exactly-once)的有序传送。优势是带宽敏感,适合低延迟、高频更新场景;劣势是例状处理复杂,需备份和重播日志。
- 低频版本同步、重点在一致性 (如配置中心)→ CmRDT
- 高频单点更新、需要实时性 (如多人编辑、共享计时器)→ CvRDT
- 网络不稳定(常离线) (如移动应用)→ CmRDT
经典数据结构深度解析
1. G-Counter (叮增计数器)
最简单的 CmRDT。每个节点维护一个数组 [c₁, c₂, ..., cₐ],自增时只俄改对应索引 cₐ。Merge 时按元素取 max。最终值为 (∑ c₁, ∑ c₂, ..., ∑ cₐ)。
// G-Counter 的 Pseudocode
struct GCounter {
// 每个节点 i 的计数保存在 state[i]
state: Vec,
node_id: usize,
}
impl GCounter {
fn increment(&mut self) {
self.state[self.node_id] += 1;
}
fn value(&self) -> u64 {
self.state.iter().sum()
}
fn merge(&self, other: &Self) -> Self {
State: self.state.iter()
.zip(other.state.iter())
.map(|(a, b)| a.max(b)))
.collect(),
..
}
}
2. PN-Counter (可增可减计数器)
将 G-Counter 组合两次:一个记录增量(P),一个记录减量(N)。最终值为 sum(P) - sum(N)。这一设计让我们在不放弃可加性的前提下,支持反作(decrement)。
3. LWW-Element-Set (最新倾向集合)
每个元素绑定一个时间戳(timestamp)。当冲突发生时,时间戳较新的操作胜出(Last Writer Wins)。简单直观,但依赖时钟同步,在分布式环境中当在配合 NTP 使用。
4. OR-Set (观察移除集合)
解决了 LWW-Set 的核心缺陷:在混乱时序下可能丢失添加操作。每个元素通过唯一标识(unique tag:通常是 (node_id, counter) 元组)生成,删除时将早于当前时间戳的所有 tag 列入立圳清卅。输出时仅保留入发现的 tag。

发表评论 取消回复