复制与一致性模型
数据复制到多台机器后,"读到的值"取决于副本的状态——一致性模型定义各副本间可以差多少。本章讲线性一致性、顺序一致性、因果一致性、最终一致性的定义与区别,每种模型允许什么异常、需要什么协调,以及 CRDT 如何在无协调下收敛(→ 复制与分片)。
一致性模型的谱系
Section titled “一致性模型的谱系”从强到弱:
| 模型 | 承诺 | 需要的协调 | 允许的异常 |
|---|---|---|---|
| 线性一致 | 每个操作在调用与返回之间原子生效,全局顺序与实时顺序一致 | 最强(多数派 + 实时钟) | 无 |
| 顺序一致 | 所有进程看到同一个全序,但不必与实时顺序一致 | 强(全序) | 时间戳上看"乱序" |
| 因果一致 | 因果相关的操作按序可见,并发的可以乱序 | 弱(只对因果对) | 无关操作的乱序 |
| 最终一致 | 停止更新后副本最终收敛 | 无 | 任意旧读 |
线性一致性:像单机一样
Section titled “线性一致性:像单机一样”线性一致性 = 像单机一样:每个操作看起来在调用与返回之间的某个瞬间原子地生效,且全局顺序与实时顺序一致。读写例子:写 x=1 提交后,任何后续读必须看到 1(包括并发开始的读,若在其返回前完成)。
代价:每次读写都要跨多数派往返(协调),延迟受最慢副本约束。ZooKeeper/etcd 的读(可选线性)、etcd 的写、Spanner 的读写事务是线性一致的。需要强一致的场景:锁、选举、唯一性约束——必须线性一致,否则分布式锁会同时给两人(→ 协调服务与实践模式)。
顺序与因果一致
Section titled “顺序与因果一致”- 顺序一致性:所有进程看到同一个全序,但不要求与实时顺序一致——"像单机但允许时间倒流"。实现上仍需全序协调,实践中少用(实现难度与线性一致相当,承诺更弱)。
- 因果一致性:因果相关的操作按序可见(你回复的帖子,别人必须先看到原帖),并发操作可以乱序——"无需共识的最强模型"。代价低(副本间只交换因果向量),适合社交流、评论。读己之写与单调读由因果一致性保证。
最终一致与收敛
Section titled “最终一致与收敛”最终一致:停止更新后副本最终收敛——期间任意旧读都允许。"最终"可以是毫秒(同城复制)也可以是分钟(跨洋)。
弱到几乎无承诺,但有收敛性保证的变体:
- CRDT(无冲突复制数据类型):以限制操作类型换取无冲突合并——操作设计成可交换(commutative)与幂等的,副本以任意顺序合并都收敛:
- 计数器(G-Counter:每个节点自己的分量取 max 求和);
- 集合(OR-Set:加法带唯一标签,删除只删标签);
- 序列(文本编辑的 RGA)——协作编辑器(Figma、Google Docs 风格)的数学基础。
CRDT 如何在无协调下收敛:把"合并"变成可交换的数学运算——冲突不是被解决,而是不存在(任意顺序合并同结果)。代价是操作集受限与空间开销(标签、向量)。
读写例子串起谱系
Section titled “读写例子串起谱系”写 x=1(主库),复制延迟 100ms:
- 期间读主库(线性一致)→ 1;
- 期间读从库(最终一致)→ 0(旧值);
- 读带因果向量(因果一致)→ 0,但你的下一次读(有因果依赖)必看到 1。
选择模型 = 承诺多少顺序 vs 付出多少协调:线性一致每请求跨多数派(慢),因果一致只对因果对协调(快),最终一致无协调(最快最弱)。
- 一致性谱系 = 对"顺序"承诺多少:线性 > 顺序 > 因果 > 最终
- 线性一致性 = 像单机一样;锁、选举、唯一性必须线性一致
- 因果一致性是无需共识的最强模型;顺序一致实现难承诺弱,实践少用
- CRDT 以限制操作类型换取无冲突合并:可交换 + 幂等,协作编辑的基础
- 选择模型 = 承诺顺序 vs 付出协调延迟