“本页与操作型 CRDT的差别在同步单位和证明义务。CvRDT 传播可幂等合并的状态;CmRDT 传播 effectors,必须管理因果顺序与重复执行。两者可以实现相同用户 API,却不是在未…”
形式陈述 ​
Operation-based CRDT(CmRDT)把更新分为两阶段:prepare 在源副本读取参数和本地状态,产生可广播的 effector;effect 在每个副本修改状态。经典收敛定理通常假设:
- 每个已提交 effector 可靠地最终送达所有正确副本;
- 因果相关的 effectors 按协议规定的因果序执行;
- 并发 effectors 两两可交换;
- 每个逻辑 effector 恰执行一次。
若
同一因果闭包中的操作可按拓扑序执行;任意两个拓扑序只会交换相邻并发操作,因此得到等价状态。可靠最终交付再保证所有正确副本最终拥有相同操作集合。
“恰一次”可以由传输层提供,也可由副本持久记录全局唯一 operation ID 去重。若网络是 at-least-once,只有 effector 本身幂等或去重机制可靠时才能直接重放;并发可交换并不推出
本页与状态型 CRDT镜像对比:CmRDT 发送意图较小的 effectors,却把更多责任交给广播、缓冲和去重;CvRDT 发送可 join 的状态,对乱序与重复更宽容。未对齐这些网络条件时,二者不是等价实现。
直觉
操作型设计让每个副本重放同一份“编辑日志”。因果顺序保留用户已观察到的先后关系,并发操作则没有共同见证的顺序,数据类型必须让它们换位不影响结果。证明关注的不是消息在现实中恰好同序,而是所有允许顺序都落到同一语义。
Prepare/effect 分离防止远端重新解释源状态。例如 observed-remove set 的 remove 在 prepare 时冻结“我已经看见的 add dots”,effector 只删除这组 dots;远端不能在执行时顺手删掉后来才看到的并发 add。源端前置条件也在 prepare 时检查;effector 到达远端后应在其依赖满足时可执行,而不能重新询问一个可能已经变化的本地业务条件。
操作消息较小不意味着历史可以立即忘掉。去重 ID、因果依赖和未交付日志都占空间;在不知道操作已稳定送达全体成员前回收它们,可能把迟到副本永久留在不同状态。
例子与边界
考虑整数计数器,effector
若两个副本都恰好执行这两个逻辑事件,终值为二。然而 A 的消息在 B 被重复执行一次时,B 得三;交换律完全没有阻止重复计数。给事件附 ID
再加入朴素
消息因果有序也不代表总序。A 的第二次 increment 必须在 A 的第一次之后执行,但 B 的并发 increment 可插在二者任意位置。把 causal broadcast 误写成 atomic broadcast 会不必要地引入全局排序成本。
推论与应用
CmRDT 适合高频小更新与可用可靠广播的部署。序列插入、带唯一 dot 的集合更新和增量计数都可只发 operation;接收端仍需验证依赖已到达、operation ID 未执行,并把 effect 持久化到崩溃恢复边界。
若 effectors 对所有顺序都交换,可放宽因果交付为任意顺序;若 effectors 幂等,可放宽恰一次为 at-least-once。每项放宽都要由具体代数证明,不能从“它是 CRDT”这一标签统一获得。去重表本身若在崩溃后丢失,逻辑恰一次也随之失效,因此 event 状态和 dedup frontier 应在同一恢复协议中持久化。
参考资料
- Marc Shapiro et al., “Conflict-Free Replicated Data Types,” SSS 2011, LNCS 6976, pp. 386–400.
- Carlos Baquero, Paulo Sérgio Almeida, and Ali Shoker, “Making Operation-Based CRDTs Operation-Based,” DAIS 2014, LNCS 8460, pp. 126–140.
- Paulo Sérgio Almeida, “Approaches to Conflict-Free Replicated Data Types,” ACM Computing Surveys 56(3), Article 76, 2024, Sec. 4.