Skip to content

CAP 与 BASE 理论 ​

S6-013建议学高级约 45 分钟JDK 21Java 高级与架构 · 分布式

1. 本节定位 ​

CAP 是背得最熟、用得最糊的理论。本节不谈定义,直接做一次「网络分区」实验:

★ 三副本库存(10 件),分区期间两侧各卖 1 件,三种取舍的一手实测 ✓✓:

text
CP  :A 侧拒写 ✗(可用性下降)→ 恢复后 A=9 B=9 C=9,**无冲突** ✓✓
AP  :两侧都写成功 ✓ → 恢复后 A=9、B=9 —— **实际卖出 2 件、库存只扣 1 件:超卖** ✗✗✗
BASE:两侧写 + 记操作日志 → 恢复后合并(-1 与 -1)→ A=8 B=8 C=8 **收敛** ✓✓

2. 学习目标 ​

  • 说清 CAP 为什么是「P 必选、C 与 A 二选一」★★★;
  • 能对业务字段判断该走 CP 还是 AP ★★★;
  • 理解 BASE 与「最终一致 + 对账」的关系 ★★★;
  • 能在架构评审里指出「这里会超卖 / 这里可以容忍」★★★。

3. 前置知识检查 ​

4. 为什么需要这个知识 ​

★ 只要数据存在两个地方,就有「同不同步」的问题 ✗✓:微服务拆开、缓存与库、多机房多活 —— 而网络一定会断(交换机重启、光纤被挖、云厂商抖动)✓✓:

场景问题的样子
秒杀库存拆分两侧都卖 → 超卖 ✗✗✗
缓存与数据库更新不同步 → 读到旧值 ⚠️
多活机房双写冲突 → 数据互相覆盖 ✗

5. 核心概念 ​

5.1 CAP 的正解:P 是必选项 ★★★ ​

字母含义现实
C Consistency所有节点读到的数据一致分区时可以放弃
A Availability每个请求都能得到响应(不报错)分区时可以放弃
P Partition tolerance网络分区时系统仍能运行必须选(网络一定会断)✓✓✓

★★ 「三选二」是错的 ✓✓✓:P 不是选项,是前提 —— 真正要选的是:分区发生时,牺牲一致性(AP)还是牺牲可用性(CP) ✓✓。

5.2 CP 与 AP 的实测对照(本机三副本)★★★ ​

★ CP:少数派拒写 ✓:

text
网络分区:A | B+C
A 侧客户端「减 1」:多数派需要 2/3 确认,A 只有自己 1 票 → **拒绝写入** ✗(可用性下降)
B 侧客户端「减 1」:B+C 两个确认 → 提交成功 ✓(同步复制给 C)
分区期间:A=10 B=9 C=9 ← A 还是旧值,但**没有产生冲突**
分区恢复:A 从多数派同步 → A=9 B=9 C=9 ✓✓ 三方一致、账实相符

★ AP:两侧都写,恢复后超卖 ✗✗✗:

text
A 侧客户端「减 1」:本地直接成功 ✓(不等其他人)
B 侧客户端「减 1」:本地直接成功 ✓
分区恢复:A=9(10-1)、B=9(10-1),两个减 1 互相不知道
→ **实际卖出 2 件、库存只扣了 1 件:超卖 1 件** ✗✗✗

★★★ 一句话选型 ✓✓✓:

字段性质选择例子
不能超卖 / 不能双花 ✗✗✗CP(宁可拒绝写)库存、余额、优惠券、分布式锁
可以容忍冲突 / 后到覆盖 ⚠️AP(宁可暂时不一致)购物车、点赞、非关键缓存
要最终一致 ✓BASE(写日志后收敛)订单状态、多活、计数器

5.3 BASE:不是「不要一致」,而是「不要求实时一致」★★★ ​

★ BASE 实测(操作日志收敛) ✓✓:

text
分区期间:A 侧记 [减1]、B 侧记 [减1](不直接改值)
分区期间读取:A 侧读到 9、B 侧读到 9 ← **各读各的视角**(不一致窗口)⚠️
分区恢复:合并操作日志(-1 与 -1,共 -2)→ A=8 B=8 C=8 ✓✓ 收敛且无超卖
BASE 字母含义本节的体现
BA Basically Available基本可用(允许降级)分区期间两侧都能写 ✓
S Soft state中间态允许存在收敛前读到 9 / 9 ⚠️
E Eventually consistent最终一致合并日志后三方相等 ✓✓

★★ 代价与前提 ✓✓:收敛前必须有「冲突合并规则」(操作日志 / 版本号 / LWW), 并且要有「对账」兜底(《分布式事务》 的最后一节)✓✓。

5.4 落到 Java / 中间件上 ★★★ ​

组件 / 用法倾向说明
ZooKeeper / etcdCP ✓选主与配置:宁可短暂不可用,不可分脑
Redis 主从(默认异步)AP ⚠️主挂了可能丢最后几条写(要 WAIT 换取更强一致)
Redis 集群 + Redlock偏 CP(有争议)⚠️分布式锁场景要知道它的边界
MySQL 主从半同步之间 ✓semi-sync 是折中:至少一个从库确认
Kafka acks=all + ISR偏 CP ✓min.insync.replicas 决定能忍几个副本掉队
缓存与库的最终一致BASE ✓先更库再删缓存 + 延迟双删 + 对账(《缓存穿透、击穿与雪崩》)

6. 递进示例 ​

6.1 一句话说清三种模式 ​

text
CP  :宁可拒绝服务,不可数据打架 → 少数派拒写
AP  :宁可持续服务,容忍数据冲突 → 都写,冲突交给业务/合并
BASE:都写,但记「意图」而不是「结果」→ 恢复后按意图收敛

6.2 业务拷问法 ​

text
问 1:这个字段错了会怎样?(超卖赔钱 → CP;点赞数不准 → AP)
问 2:能接受多长时间的「不一致窗口」?(秒级 → BASE 够;必须实时 → CP)
问 3:谁来修最后的不一致?(有对账 → BASE 敢用;没有 → 老老实实 CP)

6.3 反例:把 CAP 当「三选二」背 ​

text
「我们的系统选了 CA」✗✗ ← 分布式系统里没有 CA:
一旦发生分区,CA 系统就是「既不一致、又不可用」(两边都写或不写都错)✓✓

7. 完整可运行代码与实测 ​

代码:labs/java-deep/src/main/java/cn/kb/learn/deep/distributed/ConsistencyModelLesson.java

实测输出:

text
===== 1. CP(一致性优先):分区期间少数派拒写 =====
  网络分区:A | B+C(A 侧被隔离)
  A 侧客户端「减 1」:多数派需要 2/3 确认,A 只有自己 1 票 → **拒绝写入** ✗(可用性下降)
  B 侧客户端「减 1」:B+C 两个确认 → 提交成功 ✓(同步复制给 C)
  分区期间:A=10 B=9 C=9 ← A 还是 10(旧值,但**没有产生冲突**)
  分区恢复:A 从多数派同步 → A=9 B=9 C=9 ✓✓ 三方一致、账实相符

===== 2. AP(可用性优先):两侧都写 → 恢复后超卖 ✗✗ =====
  分区期间:A=9 B=9 C=9 ← 两侧都自认为「卖出了 1 件」
  → **实际卖出 2 件、库存只扣了 1 件:超卖 1 件** ✗✗✗

===== 3. BASE(基本可用 + 最终一致):操作日志收敛 =====
  分区恢复:合并两侧操作日志(-1 与 -1,共 -2)→ A=8 B=8 C=8 ✓✓ 收敛且无超卖

★ 读法 ✓✓:① CP 输掉的是 A(拒绝写);② AP 输掉的是 C(超卖); ③ BASE 两头都不输硬,靠「窗口 + 合并规则 + 对账」把一致性推迟到未来 ✓。

8. Debug 观察点 ​

  1. 跑三种模式,逐行对照「分区期间」与「恢复后」两个状态 ✓✓✓;
  2. 把 AP 的合并规则改成「取 max」:观察超卖变成少卖 ✗(换一种错);
  3. 给 BASE 加版本号:模拟 LWW 合并,观察"后写覆盖"的取舍 ✓;
  4. 问自己:项目 B 的秒杀库存走的是哪种?(《项目 B:高并发场景与架构设计》:Redis 预扣 + 异步落库 = BASE + 对账)✓✓。

9. 常见错误 ​

#错误说明
1背「CAP 三选二」 ✗✗✗P 必选;实际是 C vs A 二选一 ✓✓
2说「我们选了 CA」 ✗✗✗分布式里 CA 不存在(分区一来两头都不保)✓
3库存用 AP 双写 ✗✗✗一定超卖;秒杀必须预扣 + 串行化 ✓✓
4以为 BASE 等于「不保证一致」 ✗✗最终一致 + 冲突规则 + 对账,缺一不可 ✓✓
5忽略「不一致窗口」的用户体验 ⚠️窗口内用户可能看到旧值(要设计成「处理中」)✓

10. 动手实验 ​

  1. 跑 ConsistencyModelLesson 三段实验 ✓✓✓;
  2. 给 AP 增加「写版本号 + 冲突记录」,统计冲突数量 ✓✓;
  3. 把 CP 的多数派写成 1/3:观察「可用性回来了,但冲突也回来了」✗✓;
  4. 用项目的 Redis 写一个「缓存与库」的最终一致小实验(先更库 → 删缓存 → 延迟双删)✓✓。

验收方式:给定一个跨服务操作,能说出:这个字段该 CP 还是 AP、为什么、代价是什么、谁来兜底。

扩展任务:写一份《项目 A/B 的字段一致性清单》:字段 → 选择 → 理由 → 兜底手段 ✓✓

11. 练习题 ​

  1. 为什么说「P 是必选项」?
  2. CP 与 AP 各牺牲什么、换回什么?
  3. BASE 的三个字母分别是什么?与 AP 什么关系?
  4. 库存和购物车分别该走哪种?为什么?
  5. 「不一致窗口」是什么?怎么让用户不误会?
参考答案(要点)
  1. 网络一定会分区,系统无法拒绝 P;能选的只有分区时的行为 ✓✓★
  2. CP 牺牲可用性(少数派拒写)换一致性;AP 牺牲一致性(可能冲突)换可用性 ✓✓★
  3. 基本可用 + 软状态 + 最终一致;是对 AP 的工程化延伸(加「收敛」)✓✓★
  4. 库存 CP(超卖赔钱);购物车 AP(合并冲突可接受)✓✓★
  5. 收敛前各读各的视角;把状态设计成「处理中」并给最终态兜底 ✓✓★

12. 掌握标准 ​

  • [ ] 说清 P 必选、C/A 二选一 ★★★;
  • [ ] 能对字段做 CP/AP 判断 ★★★;
  • [ ] 理解 BASE 与对账的关系 ★★★;
  • [ ] 知道常见中间件的倾向 ★★★;
  • [ ] 能在评审中识别「会超卖」的设计 ★★★。

13. 扩展资源 ​

14. 下一步学习 ​

《分布式事务》 分布式事务:把「最终一致」落到幂等、补偿、对账的工程实现。 本节与 《Seata 分布式事务》(Seata 方案对比)配合:先选方案,再补机制。

前置知识

无,可直接开始

进度只保存在本机浏览器,可随时清除

个人学习项目:AI 生成内容均经人工复核,第三方资源仅作索引