跳到主要内容

一致性模型、Quorum 与线性一致

一致性模型规定并发读写允许观察到什么结果。复制副本数量和 Quorum 只描述请求要等待多少节点,系统是否线性一致还取决于版本、领导权、并发冲突和故障恢复协议。

1. 线性一致保留真实时间顺序

如果写操作已经向客户端返回成功,之后开始的读取必须看到该写或更晚结果。并发操作可以按某个瞬间排列,每个操作看起来在调用与返回之间一次生效。

线性一致适合锁、主节点选举、余额状态和唯一名称等需要实时单一结果的场景。网络分区时,无法联系足够节点的一侧通常要拒绝或等待,不能同时承诺返回最新值。

2. 顺序一致不要求尊重墙上时间

顺序一致要求所有节点观察到同一个全局操作顺序,并保留每个客户端自己的程序顺序,但这个全局顺序不必符合不同客户端操作的真实先后。

因此,一个写已经在现实时间返回,另一个稍后发起的读取仍可能在全局排列中位于它之前。它比线性一致弱,不能直接承担所有实时协调语义。

3. 因果一致保留因果关系

如果 B 的产生依赖 A,例如用户先发帖再回复,那么所有观察者都应先看到 A 再看到 B。彼此没有因果关系的并发操作可以在不同副本以不同顺序出现。

因果上下文、版本向量或会话信息可帮助跟踪依赖。它比全局线性顺序更容易跨地域扩展,但应用要能处理并发结果。

4. 会话保证改善单用户体验

最终一致系统可以提供:

  • read-your-writes:用户能读到自己的已完成写入。
  • monotonic reads:后一次读取不会退回更旧版本。
  • monotonic writes:同一会话写入按顺序生效。
  • writes-follow-reads:写入不会基于比刚读到状态更旧的版本。

可以通过粘性副本、会话版本 token 或等待副本追上实现。它改善单会话体验,不代表所有客户端看到同一个最新状态。

5. 最终一致要求停止写入后收敛

若没有新的更新,所有可用副本最终会收敛到某个结果。定义还需要补充冲突如何解决、多久收敛、读到旧值时应用做什么。

“最终一致”不保证收敛时间,也不保证业务结果正确。Last-write-wins 可能按偏移时钟丢掉一个并发更新;计数、集合等数据可以使用可交换操作或 CRDT 保留更多意图。

6. Quorum 利用副本集合相交

设副本数为 N,写等待 W 个副本,读查询 R 个副本。经典条件 W + R > N 让任意读集合与已确认写集合至少相交;W > N/2 让两个写 Quorum 相交。

相交节点可能保存多个版本,读方还要识别哪个更新更新、处理并发版本,并修复旧副本。超时写可能已到达部分副本,客户端也不知道它是否成功。

因此,仅配置 N=3、W=2、R=2 并不足以自动获得线性一致。协议还需处理写顺序、任期或时间戳、读确认和故障切换。

7. Leader 也要证明自己仍有领导权

单 Leader 可以给写入排序,但旧 Leader 在网络分区后可能仍认为自己有效。写入需要多数派或租约确认,读取要经过 Leader 并确认当前 term,或使用 ReadIndex、有效 lease 等协议。

从异步 follower 读取通常只能得到有界或无界旧值,除非读取等待它追到指定 commit index。API 应明确哪些读需要 linearizable,哪些可接受 stale。

8. CAP 讨论的是发生网络分区时的选择

CAP 中的 consistency 通常指线性一致类的单一最新视图,和数据库 ACID 中保持业务约束的 consistency 不是同一个概念。

网络分区发生时,一侧若无法确认自己是否拥有最新状态,只能拒绝部分请求来保持线性一致,或继续响应并接受分歧。没有分区时,系统仍可同时正常响应和保持一致,主要比较延迟、成本和故障概率。

9. 常见问题

9.1 强一致是一个精确定义吗

通常不是。设计文档应明确是否指线性一致、串行化事务、同步复制或写后读。不同定义允许的历史不同,不能只写“强一致”。

9.2 读写都走多数派是否一定读到最新值

集合会相交,但还要识别最新已提交版本,并处理并发写、旧 leader 和结果未知。正确的 quorum 协议不只是向多个节点并行请求后取任意响应。

10. 面试题

10.1 Quorum 的 W + R > N 为什么仍不能单独证明线性一致

出现公司:字节跳动、百度、美团、滴滴

考察重点

  • 线性、顺序、因果、会话与最终一致。
  • Quorum 交集、版本选择和结果未知。
  • Leader 任期、读确认和 CAP 分区选择。

相关内容:第 1 节“线性一致保留真实时间顺序”至第 8 节“CAP 讨论的是发生网络分区时的选择”。

参考回答

W + R > N 只保证读集合与已确认写集合有节点相交。读方还要知道相交节点上的版本是否已提交、如何比较并发写,协议也要防止旧 Leader 在新任期继续服务。一次超时写可能到达部分副本,不能仅取多数响应中的任意值。

线性一致要求已完成写之后开始的读看到该写或更新结果。实现通常还需要任期或版本规则、写入多数确认,以及 Leader 在读时证明自己仍拥有领导权,或用 ReadIndex 等方式确认 commit。能接受旧值的查询可以读 follower,并通过会话 token 或 commit index 提供 read-your-writes,降低延迟和多数派依赖。