Quorum 读写机制如何保证一致性?
简化版
在 N 个副本中,写等待 W 个、读查询 R 个;若 R+W>N,则任一读写 quorum 至少相交。 但“有交集”不自动等于线性一致:系统还必须识别最新版本、处理并发写、失败写、读修复和成员变化。 Leaderless quorum 常提供可调一致性与最终收敛;Raft 的多数派提交则有日志顺序和任期规则,二者不能混为一谈。
详细版
N=3、W=2、R=2 时读写集合必有至少一个共同副本。 客户端并行读取 R 个响应,按版本向量、逻辑时钟或协议版本选择/合并,并修复旧副本。 如果只按物理时间戳取最大值,时钟漂移可能让旧写覆盖新写。
Sloppy quorum、hinted handoff 或故障时临时副本会改变相交前提。 即使 W 返回成功,另一个并发写也可能在不同副本集合成功,形成 siblings。 需要线性一致时通常使用单 Leader 共识、条件写或更强协调,而不是只套 R+W>N 公式。
完整版教学
一、quorum 交集公式
共有 N 个固定副本,写至少成功 W 个,读至少获得 R 个。 当 R+W>N,任何读集合与已成功写集合必有交点。
N = 3, W = 2, R = 2
write replicas: {A,B}
read replicas: {B,C}
intersection: {B}
交点让读有机会接触到已写版本,但还需正确识别它。
二、W 与 R 的取舍
| 配置 | 特点 | 风险 |
|---|---|---|
| W=1,R=3 | 写快,读慢 | 单副本写可能丢 |
| W=2,R=2 | 较平衡 | 读写都需多数 |
| W=3,R=1 | 写慢,读快 | 任一副本故障影响写 |
| W=1,R=1 | 高可用低延迟 | 可能读旧且不相交 |
选择还要看故障域、尾延迟和是否允许冲突。
三、为什么交集还不够
记忆钩子:
R + W > N只证明读集合与写集合相交;要从交集里选出正确版本,还必须有版本排序、冲突处理,并明确失败写是否可能留下可见数据。
读从交点拿到新版本,也可能从其他副本拿到旧版本。 客户端需要可比较版本,并按语义选择最新或合并。 两个并发写若没有因果关系,简单大小比较可能丢失一个合法更新。 因此 vector clock、版本向量、LWW、CRDT 或业务合并策略决定最终结果。
四、物理时间戳的陷阱
节点 A 时钟快 5 秒,先写入值 old;节点 B 后来写 new,但时间戳更小。 LWW 按物理时间可能永久保留 old。 HLC 可改善排序与因果关联,但冲突语义仍需明确。 资金与唯一约束通常不能接受任意 LWW。
五、失败写和读修复
客户端只收到 1/2 个所需写响应时会报失败,但那个副本可能已经保存新值。 之后读取可能看见该版本,因此失败状态也可能不确定。 读请求发现副本版本不同时,可同步或异步 read repair。 后台 anti-entropy 负责无人读取的冷数据收敛。
六、Sloppy Quorum 改变前提
目标副本不可达时,一些系统把写临时存到非目标节点并做 hinted handoff。 这提高可用性,但“任意 W 与任意 R 必相交”的固定集合证明不再直接成立。 恢复前读取目标副本可能看不到临时写。 文档必须说明 quorum 是针对首选副本还是任意健康节点。
七、与 Raft 多数派的区别
Raft 多数派不是简单多版本读取。 它由单 Leader、term、日志前缀和提交规则形成唯一有序日志。 普通 Follower 读仍可能旧,线性读要 ReadIndex 或租约。 Leaderless quorum 更强调并发版本和冲突合并,语义通常弱于共识状态机。
八、常见误区与追问
- 误区:R+W>N 就必然线性一致。 它只证明集合相交,还缺版本与实时顺序协议。
- 误区:W 成功后所有副本都有新值。 只有至少 W 个确认。
- 误区:时间戳最大永远是最新业务值。 时钟漂移和并发写会破坏判断。
- 误区:读修复能覆盖所有冷数据。 长期不读的数据需要 anti-entropy。
- 追问:W 失败代表没写吗? 不一定,部分副本可能已写入,重试要幂等并合并版本。
- 追问:sloppy quorum 有什么代价? 临时副本提高可用性,但削弱固定相交保证并增加收敛窗口。
- 追问:什么时候选共识而非可调 quorum? 需要唯一顺序、条件写和线性一致时更适合共识。
九、加强记忆
- R+W>N 只保证固定副本集合的读写相交。
- 交点之后还要版本比较和冲突处理。
- 失败写可能部分落盘,读修复与反熵负责收敛。
- Sloppy quorum 会改变证明前提。
- Leaderless quorum 与 Raft 多数提交不是同一种一致性机制。