一支五人探险队,每人背包里都揣着一份手绘地图。队长半夜改了路线,在营火边喊了一嗓子:“新路线往东!听到的应一声!”结果只有三个人懒洋洋地应了。第二天你出发前问路,也懒得把全队叫醒,只拉住路过打水的三个人打听——巧的是,他们报出的路线居然和队长改的一模一样。为什么这么巧?因为全队五个人里,至少三个人听到了队长的更新,而你问的三个人中,必定有一位是昨晚应过声的。这个朴素的人数规则,就是分布式系统里大名鼎鼎的 Quorum(法定人数)机制:数据存 N 份副本,写入至少让 W 份确认,读取至少问 R 份,只要 R+W>N,你问的人里必有人刚确认过那次写入——最新数据,想躲都躲不开。 读到这里你可能会想:这不就是一道“小学奥数级别的抽屉原理”题吗?确实,Quorum 的数学核心就这么简单。但它的威力在于:它把“所有副本都必须参加读写”(强一致但脆弱)和“随手找一份副本读写”(快但可能读到旧值)这两个极端,用一根可以拨动的刻度尺连了起来。调节 W 和 R,本质就是在调节系统在一致性、可用性、性能之间的站位。而它与 Paxos、Raft 这些共识协议的关系,以及在 Cassandra、DynamoDB、ZooKeeper 里的实战用法,才是后端工程师真正要啃的部分。

岗位关联

学会 Quorum,你就能在设计分布式存储时随手写出“该配几个副本、读写各要几个确认”的参数组合;在系统设计面试中,也能一眼看穿面试官埋下的“强一致 vs 可用性”陷阱题——这是后端架构师的基本功。

目录
  1. 可视化演示
  2. 起源与思想
  3. 读写条件推导
  4. 读写配比策略
  5. 与共识协议关系
  6. 一致性与可用性
  7. 工程实践应用
  8. 缺陷与演进
  9. AI 应用场景
  10. Python 演示
  11. 常见误区
  12. 面试题

可视化演示

展示分布式Quorum的写入确认流程、读取交集保证与NWR配比策略,切换模式查看读写一致性原理。

本节配有交互动效(进入视口后自动加载,约 3KB)。

Quorum写入确认流程 客户端向N个副本中W个发送写入请求 收到W个ACK确认即写入成功,余下异步同步 R + W > N 写入需W个副本确认保证持久性

起源与思想

探险队最初只有一张祖传地图,队长把它卷在牛皮筒里,睡觉都搂着。可一次渡河时竹筏翻了,地图被水冲走,全队原地干瞪眼——这就是“单点存储”的脆弱。于是他们请来城里最好的抄写员,一口气誊了五份,每人背包里塞一份。地图是防丢了,新的麻烦却来了:队长每天都要改路线,五份副本改着改着就对不上号。第二天问路,这个说往东、那个说往北,到底听谁的?

分布式系统遇到的正是同一个困境:把数据复制到 N 台机器上,解决了单点故障,却引入“副本不一致”的新问题。早期方案简单粗暴——每次读写都让全部 N 个副本参与。一致是彻底了,可只要一台机器慢一点、挂掉,整次写入就得无限等待或直接失败,系统可用性被单台机器绑架。反过来,随手读写任意一台副本,速度快了,读到的却可能是一份过期的地图。Quorum(法定人数)思想,就是在两个极端之间画一条可调节的刻度线。

Quorum 的数学直觉来自“鸽巢原理”(pigeonhole principle):N 只鸽子飞进 N-1 个鸽笼,必有一个笼子住了至少两只。映射到副本世界:写入覆盖的副本集合 A,和读取询问的副本集合 B,只要二者规模之和超过总副本数 N,这两个集合必定有公共成员。A 里有一位“刚收到最新路线消息的队员”,B 里恰好问到了他,最新值就自然而然地浮出水面。这一条原理,是后面所有 Quorum 公式的地基。

探险队后来把这条规矩刻在营地石头上:“改路线至少三人应声,问路至少问三人。”听着像老练领队的经验之谈,实则就是鸽巢原理在生活里的投影——从单张地图的脆弱,到五份地图的混乱,再到“三个确认、三个询问”的秩序,Quorum 的起源,正是人类面对多份副本时朴素的仲裁直觉。

∣A∩B∣≥∣A∣+∣B∣−N\displaystyle |A \cap B| \geq |A| + |B| - N
这个公式是说:两个副本集合的交集大小,至少等于它们各自大小之和减去总副本数。只要这个差值大于 0,就说明两个集合必然重叠。
  1. A 与 B 的定义A 是写入时被更新的一组副本,B 是读取时被查询的一组副本,它们是全体 N 个副本的子集。
    A={写入覆盖的副本},B={读取询问的副本}A = \{ \text{写入覆盖的副本} \}, \quad B = \{ \text{读取询问的副本} \}
  2. 交集下界的推导A 与 B 的并集不可能超过全部副本 N,由容斥原理可得交集至少为两者大小之和减去 N。
    ∣A∩B∣≥∣A∣+∣B∣−N|A \cap B| \geq |A| + |B| - N
  3. 非空条件当且仅当 |A| + |B| > N 时,交集下界大于 0,A 与 B 的交集非空。
    ∣A∣+∣B∣>N  ⟹  A∩B≠∅|A| + |B| > N \implies A \cap B \neq \varnothing

读写条件推导

现在把地图更新想象成队长在营火边喊话。他不需要确认全队五人都听见——夜里只要有三个队员在火堆边应了声,他就当这消息“传达到位”了。第二天你想知道路线,也不打算把全队问个遍,只拉住路过打水的三个人打听。这里藏着两个关键数字:队长至少让 W=3 个人听清(写确认数),你至少问 R=3 个人(读询问数)。只要 3+3>5,你问的三个人里,必定有至少一位昨晚应过声的家伙。

把这套人数规则搬到分布式存储里,就得到 Quorum 的标准表述:系统共 N 份副本;一次写操作必须得到 W 个副本的成功确认,才向客户端返回“写入成功”;一次读操作必须收集 R 个副本的返回,才向客户端返回“读取成功”。这里的 W 叫写法定人数(write quorum),R 叫读法定人数(read quorum),约束条件就是 R+W>N。这是一场“读写听证会”:写是队长喊话,读是队员打听,人数安排决定了消息的可靠程度。

光有交叠还不够,副本之间得能判断“谁更新”。做法是给每个值附带版本号(version):写入时版本号加一,写入的 W 个副本同时存下新版本;读取时把收集到的 R 个版本拿来比较,版本号最大的就是最新值。因为读写集合必然相交,最新那笔写入的版本号一定出现在返回值里,“必读到最新值”就水到渠成。注意,这里保证的是“最新一次已确认写入”;至于并发的两笔写谁先谁后,Quorum 本身不负责裁决,那是后面要讲的共识协议或冲突处理机制的事。

探险队的规矩最后凝成一句口诀:“确认人数 + 询问人数 > 总人数。”这句话不需要懂数学也能背,但它恰恰是 Quorum 的全部数学核心——后面的配比策略、可用性分析、协议对比,都是这句人数口诀在不同场景下的变奏。

R+W>N\displaystyle R + W > N
这一条就是 Quorum 的全部核心:读法定人数与写法定人数之和大于副本总数,读写集合必然相交,最新值必被读到。
  1. 三个参数的语义R 为读法定人数,W 为写法定人数,N 为副本总数,三者都是正整数。
    R,W,N∈Z+R, W, N \in \mathbb{Z}^{+}
  2. 读写集合的规模关系一次写至少覆盖 W 个副本,一次读至少触及 R 个副本,二者规模之和超过 N。
    ∣Write∣≥W,∣Read∣≥R,∣Write∣+∣Read∣>N|Write| \geq W, \quad |Read| \geq R, \quad |Write| + |Read| > N
  3. 交集必非空由鸽巢原理,两个规模之和超过 N 的副本集合必有重叠,于是读到的 R 个版本里至少包含最新版本。
    ∣Write∩Read∣≥∣Write∣+∣Read∣−N>0|Write \cap Read| \geq |Write| + |Read| - N > 0

读写配比策略

探险队里有一类“路痴型”队员,出门前总要拉着人反复问路;还有一类“画图狂”队员,整天抢着记录第一手路线。听证会的人数不必固定,完全看全队的阅读习惯和更新频率:路痴多,就把“确认人数”往高调,让新路线尽可能多人知道;画图狂多,就把“问询人数”往高调,保证随便问几个人就能拼出全貌。

同理,R 和 W 不是非得相等。R+W>N 只是约束,在满足约束的前提下,可以按读写比例分配成本。读多写少的系统,把 W 调大、R 调小,读一次只问一两个副本,快;写多读少的系统,把 R 调大、W 调小,写只写一两份就返回,高效。很多工程师误以为 Quorum 就是“多数派”(W=R=一半加一),其实多数派只是读写频率相等时的特例。

如果设读请求占比为 p_r、写请求占比为 p_w,单次读要花 R 个副本的代价、单次写要花 W 个副本的代价,那么系统平均单次操作成本就是 p_r·R + p_w·W。调参的目标就是在 R+W>N 的约束下最小化这个加权成本:让“贵”的操作少用副本。例如 N=5、读频率是写的好几倍,选 W=4、R=2,读只需问 2 个副本;若写频率极高,则选 W=2、R=4。

“读多就把确认人数堆在写侧、问询人数压到最低”——就像路痴多的探险队,队长每次变更路线要喊到几乎全队听见,出门前却只需问离自己最近的一两个人。配比即权衡,这正是 Quorum 的工程智慧。

min⁡R,W(pr⋅R+pw⋅W)s.t.R+W>N,1≤R≤N,1≤W≤N\displaystyle \min_{R, W} \left( p_r \cdot R + p_w \cdot W \right) \quad \text{s.t.} \quad R + W > N, \quad 1 \leq R \leq N, \quad 1 \leq W \leq N
这个优化式子在说:读写各自要承担副本成本,按请求频率加权后合计起来最小,同时不能破坏 R+W>N 这条铁律。
  1. 占比归一p_r 是读请求占所有请求的比例,p_w 是写请求占比,二者相加等于 1。
    pr+pw=1p_r + p_w = 1
  2. 加权成本函数平均每次操作的成本等于读比例乘以读副本数,加上写比例乘以写副本数。
    Cost=pr⋅R+pw⋅W\text{Cost} = p_r \cdot R + p_w \cdot W
  3. 约束条件R+W>N 保证读必见最新;同时 R、W 都必须在 1 到 N 之间,不能为 0 也不能超过副本总数。
    R+W>N,1≤R≤N,1≤W≤NR + W > N, \quad 1 \leq R \leq N, \quad 1 \leq W \leq N

与共识协议关系

Quorum 像是“听证会人数规则”:规定至少几个人作证才算数。Paxos 和 Raft 则是完整的“议会流程”:先要选出一位议长(选主),再由议长把决议誊抄到议会记录本(日志复制),最后全体对表(状态机应用)。听证会人数规则只是议会流程中的一个环节,而不是整个议会制度——把两者画等号,是新手最容易犯的错误。

共识协议解决的是“多个节点就一个值达成一致,且一旦达成不可推翻”的问题。它们也使用多数派(Majority),即 W=R=⌊N/2⌋+1 这种对称的 Quorum 配比,但共识协议额外包含选主(Leader election)、日志复制(Log replication)、提交判定(Commit)等完整流程。Raft 里每条日志必须被多数派节点复制才算提交,这个“多数派”就是 Quorum 思想在控制面的落地。

Quorum 与共识协议最本质的区别:Quorum 只规定“读写需要多少副本参与”,是数据面(Data Plane)的规则;共识协议解决“多个提案谁生效、顺序如何”,是控制面(Control Plane)的问题。现实系统常常把两者叠加:用 Raft 同步元数据和选主,用 Quorum 仲裁业务数据的读写。Dynamo 风格的纯 Quorum 系统因为没有共识层,并发写冲突时只能靠 Last-Write-Win 或向量时钟兜底。

探险队的听证会只解决“消息最新”,议会制才解决“决议唯一且有序”。Quorum 是共识的基石,但不是共识本身——就像“凑齐人数”是开会的前提,但凑齐人不等于议会通过了一项法律。

M=⌊N2⌋+1\displaystyle M = \left\lfloor \frac{N}{2} \right\rfloor + 1
多数派公式:M 是超过半数的最小整数。任意两个多数派集合必然相交,这是共识协议能保证“只有一个决议胜出”的数学基础。
  1. 多数派定义节点总数为 N 时,超过半数的最小整数 M 称为多数派规模。
    M=⌊N2⌋+1M = \left\lfloor \frac{N}{2} \right\rfloor + 1
  2. 多数派两两必交任意两个多数派集合大小之和为 2M,比 N 至少大 1,所以它们必有公共节点。
    2M=2⌊N2⌋+2>N2M = 2\left\lfloor \frac{N}{2} \right\rfloor + 2 > N
  3. 为什么共识需要交叠两个互相竞争的决议若都被“多数派”确认,它们至少共享一个节点;该节点会拒绝第二个冲突决议,从而保证只有一个决议胜出。
    M1∩M2≠∅M_1 \cap M_2 \neq \varnothing

一致性与可用性

听证会要求的人越多,结论越可信,但凑齐人也越难。暴风雨天可能只有两个队员响应队长的喊话,如果写入规定“至少 4 人确认”,这场路线更新就只能放弃。探险队不得不在“可信”和“能办成事”之间反复掂量——这正是分布式系统里一致性与可用性的永恒矛盾。

用 Quorum 的语言说:W 越大,读到最新值的把握越大,但写操作需要的活副本越多,遇到节点故障、网络分区时写失败的概率也越高。Quorum 的价值恰恰在于把“必须全部副本在线”放松为“必须 W 个副本在线”,用一点一致性换可用性,或者反过来。这也是 CAP 定理在工程上的具体体现:分区发生时,选择等待凑齐 W 个副本,就是选了一致性(C);选择降级写入任意活副本,就是选了可用性(A)。

假设单节点可用概率为 p,一次写要凑齐 W 个活副本,成功概率服从二项分布:N 个副本中至少有 W 个存活的概率。把各项累加起来,就能量化“W 每加一,可用性对节点稳定性有多敏感”。举个例子:p=0.9、N=3 时,W=2 的写可用率是 97.2%,而 W=3 只剩 72.9%——仅仅多等一个副本确认,可用率就掉了 24 个百分点。

探险队在好天气里开 4 人听证会求稳妥,在暴风雨里只能退而求其次开 2 人听证会“先把消息传出去”。Quorum 的可调性,就是让系统能像拨动刻度盘一样,在一致性与可用性之间滑动。

Pwrite ok=∑i=WN(Ni)pi(1−p)N−i\displaystyle P_{\text{write ok}} = \sum_{i=W}^{N} \binom{N}{i} p^{i}(1-p)^{N-i}
这个式子在算“写操作成功的概率”:把 N 个副本里恰好有 i 个存活(i 从 W 到 N)的概率全部加起来,因为只要存活副本数达到 W,写入就能成功。
  1. 单节点可用性每个副本节点正常响应的概率为 p,p 越接近 1 表示节点越稳定。
    0≤p≤10 \leq p \leq 1
  2. 二项分布单项N 个副本中恰好 i 个可用的概率,由组合数乘以成功与失败概率的幂次给出。
    (Ni)pi(1−p)N−i\binom{N}{i} p^{i}(1-p)^{N-i}
  3. 写可用率求和存活副本数 i 达到 W 或更多时写入就能成功,把所有满足条件的概率累加,得到写可用率。
    Pwrite ok=∑i=WN(Ni)pi(1−p)N−iP_{\text{write ok}} = \sum_{i=W}^{N} \binom{N}{i} p^{i}(1-p)^{N-i}

工程实践应用

不同的探险队配置不同:求稳的团队把确认人数调得很高,追求效率的团队把确认人数压得很低。现实里的分布式存储系统正是这么配置的——Quorum 不是论文里的抽象概念,而是写在配置文件里的具体数字。

Amazon Dynamo 及其开源兄弟 Cassandra 是 Quorum 的教科书级应用。它们允许为每次读写单独指定一致性级别:ALL(所有副本参与)、QUORUM(多数派参与)、ONE(单个副本参与)。这些级别背后就是 R、W、N 的组合。以 Cassandra 最常用的配置为例:N=3、W=2、R=2,此时 R+W=4>3,保证读到最新已确认数据,同时允许一个副本故障而不影响读写。

ZooKeeper 则展示了 Quorum 的反向妙用:它要求写必须同步到多数派节点,但读可以走任意节点,用“读可能拿到旧值”换取极高的读性能——它保证所有写有序,但读是顺序一致性而非线性一致。选主场景也离不开 Quorum:Etcd 的 Raft 选主要求候选人获得多数派投票才能成为 Leader,网络分区时少数派一侧即使选出了“主”也无法提交事务,从根本上避免双主。

探险队出发前要把“全队 3 人、确认 2 人、询问 2 人”写进行前手册——Dynamo、Cassandra 的默认参数背后,就是这份朴素手册的工程化版本。

N=3,W=2,R=2⟹R+W=4>N=3\displaystyle N = 3, \quad W = 2, \quad R = 2 \quad \Longrightarrow \quad R + W = 4 > N = 3
这是 Cassandra 默认配置的数学表达:三副本、读写各要两个确认,既满足 Quorum 条件,又留出了一个副本的故障余量。
  1. 三副本设定N=3 是最常见的副本数,兼顾容错与存储成本。
    N=3N = 3
  2. 写法定人数为 2写入至少 2 个副本确认成功才返回,允许 1 个副本故障时仍可写。
    W=2W = 2
  3. 读法定人数为 2读取比较 2 个副本的版本号取最新,与写集合交叠至少 1 个节点。
    R=2,R+W=4>NR = 2, \quad R + W = 4 > N

缺陷与演进

听证会也有盲区:如果队长在风暴里同时喊了两条相互矛盾的路线名(并发写冲突),或者两个副队长同时开始发号施令(脑裂),再多的听证人数也分不清谁对谁错。Quorum 能保证“必读到最新已确认写入”,但“最新”不等于“唯一”,更不等于“无冲突”。

Quorum 的三宗罪值得牢记:其一,并发写冲突时,R+W>N 只保证读到最新写入,不保证多笔写入不互相覆盖,通常要靠 Last-Write-Win(最后写胜出)或向量时钟来取舍;其二,W 次物理写造成“写放大”,W 越大,同等逻辑写入消耗的磁盘和网络带宽越大;其三,某些节点失联时,严格 Quorum 可能凑不齐 W 个副本,写入直接被拒绝——一致性守住了,可用性塌了。

工程界的演进方案都是围绕“凑不齐人数怎么办”展开的。Sloppy Quorum(乱序仲裁)在分区时临时把数据交给未持有该副本的健康节点托管,等原副本恢复再移交数据,这是 Dynamo 保持分区可用性的关键;读修复(Read Repair)在读取时发现旧值,顺手把最新值回写给旧副本;反熵协议(Anti-Entropy)在后台比对副本差异并收敛。这些方案都是在 Quorum 人数不够时用别的武器兜底,而不是推翻 Quorum。

听证会的盲区要靠“临时委托证人、事后补记录、后台对账”来补——分布式系统也正是用乱序仲裁、读修复、反熵协议,让 Quorum 在真实世界的噪声里依然可靠。

写放大倍数=W×单副本写成本客户端逻辑写成本=W\displaystyle \text{写放大倍数} = \frac{W \times \text{单副本写成本}}{\text{客户端逻辑写成本}} = W
写放大公式:一次逻辑写操作因为要确认 W 个副本,物理上被放大成 W 次副本写入,W 越大放大越严重。
  1. 基准写成本客户端一次逻辑写入若只落一个副本,成本记为 1。
    基准成本=1\text{基准成本} = 1
  2. Quorum 的写放大要 W 个副本确认,物理写就是 W 次,放大倍数为 W。
    写放大=W\text{写放大} = W
  3. 配比中的权衡W 越大写放大越严重,但读侧 R 可以相应调小;反之 W 小则读侧 R 变大,读写成本此消彼长。
    写放大∝W,读成本∝R,R+W>N\text{写放大} \propto W, \quad \text{读成本} \propto R, \quad R + W > N

AI 应用场景

分布式数据库读写路径设计

阶段:数据面一致性参数调优(系统架构设计)

解决的问题:传统单主数据库的主从复制存在“主库故障时写不可用、从库延迟时读旧数据”两大痛点。用 Quorum 配比,业务方可以为每次读写单独指定一致性级别(ONE/QUORUM/ALL),在性能和一致性之间自由摆动,而不是被“全有或全无”的复制策略绑死。

具体案例:电商订单系统把订单数据配成 N=3、W=2、R=2,一个可用区故障时自动降级为 W=1 的 Sloppy Quorum 继续承接写入,同时开启读修复在后台收敛旧副本。压测数据显示,读延迟比强同步复制方案下降约 40%,而极端故障下仍能保持写入可用。

缓存与数据库的一致性调和

阶段:多级缓存一致性设计

解决的问题:缓存与数据库之间“读漂移”——缓存更新了但数据库没跟上,或反过来。用 Quorum 思想给缓存层配置“多副本确认”:写入时同时更新数据库与足够多的缓存副本,读取时从多个缓存副本比对版本号,避免单点缓存过期数据被大量请求命中。

具体案例:某配置中心把热点配置缓存在 3 台边缘节点上,写配置要求至少 2 台确认写入(W=2),客户端读取时询问 2 台并取版本号大者(R=2)。任意一台缓存节点宕机都不影响读写,配置变更 1 秒内全局可见。

分布式锁与选主仲裁

阶段:控制面仲裁与脑裂防护

解决的问题:多实例抢锁或选主时,若只凭单节点拍板,网络分区会出现“双主”脑裂。用多数派 Quorum(W=R=⌊N/2⌋+1)保证任意时刻只有一个 Leader 能获得法定支持,从根本上杜绝双主。

具体案例:Etcd 集群 3 节点选主,候选人必须获得至少 2 票才能当选。网络分区把集群切成 1 和 2 两半时,只有 2 节点一侧能凑齐多数票;1 节点一侧即使选出“主”,写入也无法通过多数派确认,最终自动让位。

Python 演示

python54 行
import random  class QuorumKV:    """用 Quorum 机制保证读到最新值的分布式存储模拟器"""     def __init__(self, n, w, r):        assert r + w > n, "必须满足 R + W > N 才能读到最新值"        self.n = n        self.w = w        self.r = r        self.data = [dict() for _ in range(n)]        self.version = [dict() for _ in range(n)]        self.clock = {}     def write(self, key, value):        self.clock[key] = self.clock.get(key, 0) + 1        ver = self.clock[key]        for i in random.sample(range(self.n), self.w):            self.data[i][key] = value            self.version[i][key] = ver        return ver     def read(self, key):        best_ver = -1        best_val = None        for i in random.sample(range(self.n), self.r):            v = self.version[i].get(key, -1)            if v > best_ver:                best_ver = v                best_val = self.data[i][key]        return best_val, best_ver  def count_stale(n, w, r, rounds=1000):    kv = QuorumKV(n, w, r)    stale = 0    for t in range(1, rounds + 1):        kv.write("counter", t)        _, ver = kv.read("counter")        if ver < t:            stale += 1    return stale  def main():    for n, w, r in [(5, 3, 3), (5, 2, 2), (3, 2, 2)]:        stale = count_stale(n, w, r)        cond = "R+W>N" if r + w > n else "R+W<=N"        print("N=%d W=%d R=%d %s 过期读=%d/1000" % (n, w, r, cond, stale))  if __name__ == "__main__":    main()
目的:模拟带版本号的 Quorum 键值存储,验证“R+W>N 时必读到最新值、破坏该条件则出现过期读”的数学结论。
  1. assert r + w > n, "必须满足 R + W > N 才能读到最新值"构造模拟器时先校验听证会人数条件,错误配比直接报错,防止被静默放过。
  2. self.clock[key] = self.clock.get(key, 0) + 1维护每个 key 的逻辑时钟,作为版本号来源,每写一次自增。
  3. ver = self.clock[key]取出本次写入的版本号,写入的副本要同时存值和版本号。
  4. for i in random.sample(range(self.n), self.w):每次写入随机挑 W 个副本,模拟“恰好 W 个副本确认”而不是写全量。
  5. v = self.version[i].get(key, -1)从副本取版本号,没写过该 key 返回 -1,避免把“无值”当成“旧值”。
  6. if v > best_ver:读侧挑版本号最大的返回,对应“听证会采信最新证词”。
  7. if ver < t:只要返回的版本落后于刚写入的版本,就计一次过期读。
  8. for n, w, r in [(5, 3, 3), (5, 2, 2), (3, 2, 2)]:三组对照实验:前两组验证满足 R+W>N 时零过期读,第二组破坏条件后出现约三成过期读。

常见误区

误区“Quorum 就是超过一半的节点参与读写嘛,W 和 R 都应该是 N/2+1,这样最安全。”

正解多数派只是 Quorum 的一个特例。R+W>N 才是完整约束,R 和 W 可以在 1 到 N 之间自由搭配。读多写少时,N=5 配 W=4、R=2 依然是合法且更优的 Quorum;多数派仅在读写频率均衡时最优。把特例当定义,会错失配比优化的空间。

为何常见:多数派是 Paxos、Raft、选主里最常见的用法,接触多了容易形成“Quorum=多数”的思维定式。

误区“只要满足 R+W>N,任何读都能拿到最新值,这不就是强一致吗?那可以做分布式事务了。”

正解Quorum 保证的只是“读到最新已确认的写入”,比强一致弱得多。它不提供串行化、不处理并发写冲突、不能防止两笔写互相覆盖,也无法约束写入尚未完成时的读行为。线性一致(Linearizability)需要 Raft、Paxos、ZAB 这类带排序和提交判定的协议。

为何常见:“必读到最新”听起来很像强一致,望文生义是人之常情。

误区“Cassandra 用 Quorum 就能保证数据一致,说明 Quorum 就是共识算法,可以替代 Raft。”

正解Quorum 是数据面仲裁规则,解决“读能否拿到最新已确认值”;共识协议解决控制面的“多个提案谁胜出、顺序如何、且不可推翻”。Quorum 不选主、不排序、不保证并发提案只有一个生效。Dynamo 风格的 Quorum 系统在并发写时甚至会丢失最后若干次写入,因此常配向量时钟处理冲突。

为何常见:两者都讲“多数”,名字相似,概念混淆自然发生。

误区“只要配好 R+W>N,分布式系统就不会读到旧数据了,部署和运维都不用操心。”

正解该条件成立有前提:所有写入要落盘成功后再确认;读请求故障重试时不能反复采样同一批节点;并发写要有冲突处理(LWW 或向量时钟)。若可用副本不足 W 个,写入会被拒绝;若网络分区时启用 Sloppy Quorum,临时托管节点会破坏严格交叠保证。数学条件优美,工程实现处处是坑。

为何常见:公式简单漂亮,容易让人忽略真实系统里各种违背假设的边角情况。

误区“W 越大,读到最新的概率越高,那直接把 W 设成 N,最安全。”

正解W=N 意味着仲裁要求所有副本确认,任何单点故障都导致写入失败,可用性归零,同时写放大 W 倍。Quorum 的价值正是“用略大的 W 换取远高于单点的可用性”。定量看,p=0.9、N=3 时,W=2 的写可用率 97.2%,W=3 只有 72.9%。W 必须在一致性、写放大、写可用性三者间取平衡。

为何常见:直觉上“更多确认=更可信”,却忽略了故障概率随 W 增大而指数级恶化。

面试题

Q1. 在 Quorum 机制中,保证读取必定能看到最新已确认写入的核心条件是?

单选题 · easy

  • A.读副本数大于总副本数的一半
  • B.写副本数大于总副本数的一半
  • C.读写副本数之和大于总副本数← 正确答案
  • D.读写副本数之和等于总副本数

答案:C。C 正确。鸽巢原理要求写入覆盖的副本集合与读取询问的副本集合大小之和超过总副本数 N,交集才必然非空。A、B 单独成立只是单侧多数,并不能作为一般条件;D 的“等于”意味着两个集合可能恰好不相交,会读到旧值。

Q2. 某三副本系统配置为 N=3、W=2、R=2,此时最多能容忍多少个副本故障而不影响写入?

单选题 · easy

  • A.一个副本故障时仍可写入← 正确答案
  • B.两个副本同时故障仍可写入
  • C.三个副本全部故障仍可写入
  • D.只要有任何故障就写不了

答案:A。写法定人数 W=2,只要还有 2 个存活副本,写入就能成功。3 副本中坏 1 个还剩 2 个,可以写;坏 2 个只剩 1 个,写失败。因此最多容忍 1 个副本故障,对应 A。这个“2 合法、1 非法”的边界,正是 R+W>N 提供的容错空间。

Q3. 某系统 N=5、读请求占 90%,希望在满足 R+W>N 的同时把读延迟压到最低,最合理的配比是?

单选题 · mid

  • A.W=5 R=1 读最快但写无容错
  • B.W=4 R=2 读快且容忍单点故障← 正确答案
  • C.W=3 R=3 读写均衡表现平庸
  • D.W=2 R=4 写最快但读路径最重

答案:B。读多写少应尽量减小 R。W=5、R=1 读最快,但写要求全部副本确认,一个副本故障就无法写入,可用性极差;W=4、R=2 满足 R+W=6>5,读只查 2 个副本,且允许 1 个副本故障,是折中后最合理的选择。

Q4. 关于 Quorum 与 Paxos/Raft 共识协议的关系,下列说法正确的有?

多选题 · mid

  • A.共识协议选主用的多数派是 Quorum 的特例← 正确答案
  • B.Quorum 只规定读写仲裁人数不负责排序← 正确答案
  • C.Quorum 可以完全替代 Paxos 完成日志复制
  • D.Raft 的日志提交依赖 Leader 与多数派确认← 正确答案

答案:ABD。A 正确,多数派就是 R+W>N 的对称特例;B 正确,Quorum 属于数据面规则,不管提案谁先谁后;C 错误,日志复制需要选主、排序、提交判定,Quorum 做不到;D 正确,Raft 每条日志必须被多数派复制才可提交。

Q5. 在 Dynamo 风格系统中,当节点故障导致严格 Quorum 凑不齐 W 个副本时,系统临时把数据交给其他健康节点托管,这种机制称为?

单选题 · mid

  • A.读修复,读取时顺手更新旧副本
  • B.乱序仲裁,临时把数据托付给健康节点← 正确答案
  • C.反熵协议,后台比对副本之间的差异
  • D.向量时钟,用版本记录并发写的历史

答案:B。Sloppy Quorum(乱序仲裁)允许把数据写入不在原副本集合上的节点,先保证可用性,等原副本恢复再移交数据。A 是读取时发现旧值顺手修复;C 是后台定期比对副本差异;D 是并发版本控制手段,都与“临时托管”无关。

Q6. 设单副本可用概率 p=0.9,N=3,比较 W=2 与 W=3 的写可用率,正确的结论是?

单选题 · extreme

  • A.W=2 的写可用率约 97.2%,W=3 约 72.9%← 正确答案
  • B.W=2 的写可用率约 72.9%,W=3 约 97.2%
  • C.W=2 的写可用率约 90%,W=3 约 81%
  • D.W=2 和 W=3 的写可用率相同,均为 100%

答案:A。三个副本至少 2 个可用:0.9³+3×0.9²×0.1=0.972;三个全可用才能满足 W=3:0.9³=0.729。把写条件从“等 3 个确认”降到“等 2 个确认”,可用率从 72.9% 提到 97.2%,这正是 Quorum 用一致性换可用性的量化体现。

Q7. 在只有 Quorum 而没有共识协议的系统中,仍可能发生的问题是?

多选题 · hard

  • A.并发写互相覆盖造成部分更新被静默丢弃← 正确答案
  • B.读请求永远读不到已确认提交的最新版本
  • C.分区时少数派侧继续写入导致冲突难以融合← 正确答案
  • D.所有副本版本号完全一致导致无法区分新旧

答案:AC。A 是典型问题,Quorum 不处理并发写,LWW 策略下后写覆盖先写会丢更新;C 也是问题,Sloppy Quorum 下少数派可继续写入,多个分区各写各的,融合时需向量时钟才能检出冲突。B 错误,R+W>N 保证读到已确认版本;D 不成立,版本号一致说明值相同,无需排序。

Q8. Quorum 的思想来源“鸽巢原理”在机制中直接证明了哪件事?

单选题 · easy

  • A.读写两个副本集合之间必然存在重叠← 正确答案
  • B.写入延迟必然小于读取延迟
  • C.节点故障一定可以被及时检测出来
  • D.副本之间的网络传输必然畅通无阻

答案:A。N 个副本里,写入覆盖 W 个、读取询问 R 个,只要 W+R>N,两个集合大小之和超过总副本数,必然重叠——这就是“N 只鸽子进 N-1 个笼子,必有一笼两只”的翻版。其余三项都与鸽巢原理无关。

Q9. 提升一个读多写少的 Quorum 系统写入可用性的可行手段有?

多选题 · mid

  • A.满足 R+W>N 的前提下适当减小 W← 正确答案
  • B.满足 R+W>N 的前提下继续增大 W
  • C.采用乱序仲裁让健康节点临时托管副本← 正确答案
  • D.坚持等待全部副本确认成功后再返回

答案:AC。W 越小写入越容易凑齐,可用性越高,但为保持 R+W>N,读侧 R 需相应增大;Sloppy Quorum 在分区时把写入委托给非持有副本的节点,也能提升可用性。B、D 都让写入条件更苛刻,可用性必然下降。

Q10. ZooKeeper 采用“写走多数派、读走任意节点”的策略,其代价是?

单选题 · mid

  • A.读可能拿到旧值,一致性降级为顺序一致← 正确答案
  • B.写延迟大幅上升,且集群无法继续扩展
  • C.读性能严重下降,每次读取都要查询全副本
  • D.数据安全丧失,完全没有任何一致性保证

答案:A。ZooKeeper 保证所有写按顺序执行并同步到多数派,但读可打到任意节点,可能读到尚未同步的旧值,因此提供的是顺序一致性而非线性一致。这是用读一致性换读性能的经典 Quorum 变体。B、C、D 都与实际设计相悖。