跳转到内容

复制与一致性模型

数据复制到多台机器后,"读到的值"取决于副本的状态——一致性模型定义各副本间可以差多少。本章讲线性一致性、顺序一致性、因果一致性、最终一致性的定义与区别,每种模型允许什么异常、需要什么协调,以及 CRDT 如何在无协调下收敛(→ 复制与分片)。

从强到弱:

模型承诺需要的协调允许的异常
线性一致每个操作在调用与返回之间原子生效,全局顺序与实时顺序一致最强(多数派 + 实时钟)无
顺序一致所有进程看到同一个全序,但不必与实时顺序一致强(全序)时间戳上看"乱序"
因果一致因果相关的操作按序可见,并发的可以乱序弱(只对因果对)无关操作的乱序
最终一致停止更新后副本最终收敛无任意旧读

线性一致性 = 像单机一样:每个操作看起来在调用与返回之间的某个瞬间原子地生效,且全局顺序与实时顺序一致。读写例子:写 x=1 提交后,任何后续读必须看到 1(包括并发开始的读,若在其返回前完成)。

代价:每次读写都要跨多数派往返(协调),延迟受最慢副本约束。ZooKeeper/etcd 的读(可选线性)、etcd 的写、Spanner 的读写事务是线性一致的。需要强一致的场景:锁、选举、唯一性约束——必须线性一致,否则分布式锁会同时给两人(→ 协调服务与实践模式)。

  • 顺序一致性:所有进程看到同一个全序,但不要求与实时顺序一致——"像单机但允许时间倒流"。实现上仍需全序协调,实践中少用(实现难度与线性一致相当,承诺更弱)。
  • 因果一致性:因果相关的操作按序可见(你回复的帖子,别人必须先看到原帖),并发操作可以乱序——"无需共识的最强模型"。代价低(副本间只交换因果向量),适合社交流、评论。读己之写与单调读由因果一致性保证。

最终一致:停止更新后副本最终收敛——期间任意旧读都允许。"最终"可以是毫秒(同城复制)也可以是分钟(跨洋)。

弱到几乎无承诺,但有收敛性保证的变体:

  • CRDT(无冲突复制数据类型):以限制操作类型换取无冲突合并——操作设计成可交换(commutative)与幂等的,副本以任意顺序合并都收敛:
    • 计数器(G-Counter:每个节点自己的分量取 max 求和);
    • 集合(OR-Set:加法带唯一标签,删除只删标签);
    • 序列(文本编辑的 RGA)——协作编辑器(Figma、Google Docs 风格)的数学基础。

CRDT 如何在无协调下收敛:把"合并"变成可交换的数学运算——冲突不是被解决,而是不存在(任意顺序合并同结果)。代价是操作集受限与空间开销(标签、向量)。

写 x=1(主库),复制延迟 100ms:

  • 期间读主库(线性一致)→ 1;
  • 期间读从库(最终一致)→ 0(旧值);
  • 读带因果向量(因果一致)→ 0,但你的下一次读(有因果依赖)必看到 1。

选择模型 = 承诺多少顺序 vs 付出多少协调:线性一致每请求跨多数派(慢),因果一致只对因果对协调(快),最终一致无协调(最快最弱)。

  • 一致性谱系 = 对"顺序"承诺多少:线性 > 顺序 > 因果 > 最终
  • 线性一致性 = 像单机一样;锁、选举、唯一性必须线性一致
  • 因果一致性是无需共识的最强模型;顺序一致实现难承诺弱,实践少用
  • CRDT 以限制操作类型换取无冲突合并:可交换 + 幂等,协作编辑的基础
  • 选择模型 = 承诺顺序 vs 付出协调延迟