把地基打平
方案评审会上,老王被架构师一句话问住了:「你这个方案牺牲了一致性,业务侧接受吗?」他知道 CAP 里有个 C,但「牺牲一致性」到底牺牲成什么样、用户看到什么、多久恢复,说不上来。回去把三个理论重新过了一遍——这三个理论不直接解决问题,但决定你选方案时问什么问题。
ACID:单机的承诺,出域即失效
数据库教科书四件套:原子性(全成或全败)、一致性(约束永远满足)、隔离性(并发事务互不干扰)、持久性(提交即落盘)。这四条由数据库引擎在单机内一手包办,代价是锁、日志、 MVCC 那一整套机制。
拆到分布式,四条里先崩的是谁?原子性和隔离性——原子性需要「统一提交点」,三个库谁也没有权力代表全体宣布成功;隔离性需要「统一的锁管理器」,而分布式锁的性能与可靠性都是硬骨头。持久性靠各自的库还能守住;一致性则要看你愿意付多少代价——这正是 CAP 要回答的。
CAP:不是三选二,P 没得选
CAP 定理三个字母:一致性(所有节点同一时刻看到同一份数据)、可用性(每个请求都能在合理时间内得到响应)、分区容忍(网络分区发生时系统还能继续运转)。
流传最广的误读是「三选二」。纠正一下:P 不是选项,是现实——多机房、跨交换机、微服务之间的网络,分区(节点间失联)迟早发生,而且发生时你的系统必须还能工作,否则平时也没人敢用。所以真正的取舍是:分区发生的那一刻,选 C 还是选 A。选 C,分区期间拒绝服务保数据一致(银行的态度);选 A,分区期间继续服务但数据可能不一致(电商的态度)。绝大多数互联网业务选 A,这就是下文的伏笔。
BASE:中间态的合法化
选 A 的阵营给出了自己的口号 BASE:基本可用(Basically Available,故障时允许降级)、软状态(Soft State,允许数据存在中间态)、最终一致(Eventually Consistent,停止写入后经过一段时间,副本总会一致)。
BASE 的革命性在于把「中间态」从 bug 升级成设计的一部分:订单「已创建但库存未扣」这个状态不再是事故,而是一个有协议保证会收敛的合法阶段。第 1 篇里让老王睡不着觉的半路状态,BASE 给了它名分——名分不是纵容,收敛的路径(消息重投、补偿、对账)才是系列后面所有方案的真正内容。
顺带补一个阶梯感:最终一致内部也分层——强一致之上是线性一致,往下依次是单调读、会话一致、最终一致。用户视角最要紧的是单调读:自己刷新页面别看到数据「回退」。做最终一致方案时,读路径同样要设计,别只盯着写路径。
ACID 与 BASE 一张表
| 维度 | ACID | BASE |
|---|---|---|
| 一致性时点 | 提交瞬间全库一致 | 停止更新后逐步收敛 |
| 中间态 | 不允许存在 | 合法且必须有收敛机制 |
| 可用性 | 让位于一致性 | 优先保基本可用 |
| 实现方式 | 锁、日志、单机引擎 | 消息、补偿、对账 |
| 典型场景 | 单库内资金扣减 | 跨服务下单链路 |
业务视角:谁配得上强一致
理论落地成一条纪律:强一致是奢侈品,按数据的重要性发放。老王给业务分了三档:账户余额本身的扣减(用户盯着看的钱)能塞进单库单事务就别拆;库存防超卖的热点行用「单库原子扣减 + 消息异步同步」混合处理;积分、优惠券、通知、物流状态这些旁路数据,全部最终一致,容忍秒级延迟换可用性。分完档再看方案谱系,选型不再纠结——方案没有好坏,只有和业务档位匹不匹配。
小结
理论篇一句话:ACID 出了单机先崩原子性与隔离性;CAP 的 P 没得选,真取舍在 C 与 A 之间;BASE 把中间态合法化,收敛机制才是方案的灵魂。地基打平,下一站进入第一个正式方案——2PC 与 XA:它是强一致阵营的旗舰,也是理解所有「两阶段」思想的原点。
评论 (0)