CAP 定理与工程取舍
1. CAP 定理是什么
1.1 三个性质各是什么意思
CAP 常见定义下的三个性质是:
- C(Consistency,一致性):对外表现为单副本语义,常用的形式化目标是线性一致性(Linearizability)。
- A(Availability,可用性):每个请求都能在有限时间内返回响应(不要求一定成功,但不能无限等待)。
- P(Partition tolerance,分区容忍):即便出现网络分区(节点之间消息丢失/延迟到不可用),系统仍继续提供服务能力(至少对某一侧提供响应)。
1.2 CAP 的核心结论
CAP 的关键不是“只能选两个”,而是:
- 当网络分区发生时(P 发生),系统无法同时保证 C 与 A。
- 如果你要求 P(现实系统几乎都必须容忍分区),那么分区时只能在 C 和 A 之间取舍。
工程上更准确的说法是:“在分区或极端延迟下,你是选择拒绝请求以保正确性(偏 CP),还是选择继续响应但允许读到旧值/冲突(偏 AP)”。
2. 举例说明:工程里如何取舍
2.1 取舍的第一原则:先写清楚业务不变量
讨论 CAP 前,先把业务不变量(Invariant)说清楚,例如:
- 余额不能为负;同一订单不能重复扣款。
- 库存不能被卖成负数(或允许超卖但要补偿)。
- 配置变更必须全局一致(否则导致全局故障)。
不变量决定哪些路径必须要 C,哪些路径可降级为 A。
2.2 典型 CP:配置中心、注册发现、分布式锁、元数据
这些场景的共同点是:错一次会造成全局性错误,宁可不可用也不能错误。
- 配置中心:读到不一致配置会导致同一版本的服务行为分裂。
- 分布式锁:不一致会导致同时拿到锁,直接破坏互斥。
- 存储系统元数据:例如分片路由、leader 信息,错误会导致写入落错位置。
工程行为(分区时):
- 牺牲 A:返回错误或超时,提示“稍后重试”。
- 保证 C:只允许多数派(quorum)侧对外提供写入。
2.3 典型 AP:内容分发、点赞计数、推荐、监控指标
这些场景的共同点是:暂时不一致可以接受,体验比绝对正确更重要,且可通过后续修正收敛。
- 点赞/浏览计数:允许短时间不准确,后续合并。
- 推荐/Feed:允许读到旧数据,用户体验更平滑。
- 监控指标:更关注趋势,偶发缺失可接受。
工程行为(分区时):
- 牺牲 C:各分区继续写入与响应。
- 通过异步合并、冲突解决、最终一致性把数据收敛。
2.4 混合策略:同一系统不同接口不同取舍
面试最加分的点:不是“我们选 CP”或“我们选 AP”,而是按接口分级。
例子(可按你实际业务替换):
- 下单扣款:CP(强一致,失败就明确失败)。
- 订单列表展示:AP(读旧无所谓,保证可用性)。
- 风控配置:CP(配置一致性优先)。
- 埋点上报:AP(可丢、可重试、可延迟)。
3. 常见实现手段(把“口号”落到机制)
3.1 Quorum(多数派)读写:从机制上偏 CP
在 N 副本系统中,选取:
- 写入需要 W 个副本确认。
- 读取从 R 个副本读取并合并。
若满足 R + W > N,则读写集合必有交集,可以降低读到旧值的概率。
注意:R + W > N 解决的是“读到旧值”的概率与边界,但并不自动等价于线性一致性。要达到线性一致性,通常还需要 leader、单调递增日志、租约(lease)等额外约束。
3.2 Leader 复制:以单点顺序换取更强一致
典型做法:
- 选举 leader。
- 所有写入经由 leader 排序。
- follower 按日志顺序复制。
分区时:
- 只有多数派能选出 leader 并继续提供写入(偏 C)。
- 少数派无法写入(牺牲 A),通常只读或直接拒绝。
3.3 降级与兜底:以“功能退化”换取可用性
当系统无法同时满足正确性与可用性时,常见降级:
- 只读模式:写入失败但允许读取旧数据。
- 读缓存兜底:读 DB 超时则读缓存,接受旧值。
- 延迟队列补偿:写入失败先落本地日志,后续重放。
4. 面试追问:CAP 和 ACID、BASE 什么关系
4.1 CAP 关注的是“分区时系统对外承诺”
- CAP 讨论的是分布式系统在网络异常时的可用性与一致性取舍。
- ACID 是数据库事务的四个性质,强调事务语义(原子性、一致性、隔离性、持久性)。
4.2 BASE 是工程化折中
BASE 常用表达:
- Basically Available:基本可用。
- Soft state:软状态。
- Eventually consistent:最终一致。
它更像 AP 或弱一致系统的工程指导思想。
5. 参考资料(论文 / 标准)
- CAP:Brewer 提出观点;形式化证明常引用 Gilbert & Lynch, 2002。