CAP 之后:工程里真正要做的三个取舍

king
2026-11-08 / 0 评论 / 3 阅读

“CAP 三选二”大概是分布式系统里被引用最多、误解也最多的一句话。它说的是:在网络分区发生时,一致性和可用性只能保一个。注意前提是分区发生时。

绝大多数时间网络是通的,这时候的取舍是另一个问题。

一、PACELC:把“平时”也纳入考虑

PACELC 是对 CAP 的补充:

  • 如果发生分区(P),在可用性(A)和一致性(C)之间选
  • 否则(E,else),在延迟(L)和一致性(C)之间选

这更贴近日常:没有分区时,你依然要在“多副本同步写(慢但一致)”和“异步复制(快但可能读到旧值)”之间做选择。

二、取舍一:读旧数据能不能接受

以博客的评论数为例:

  • 强一致:每次评论都同步更新计数,读到的数字永远准确,但写变慢
  • 最终一致:先写评论,计数异步累加,短时间内可能少一两条

对内容站,后者几乎总是更好的选择。用户不会因为评论数晚两秒更新而离开,但会因为发布失败而离开。

判断标准是:这个数字错了,会不会导致错误决策或资金损失? 不会,就允许旧。

三、取舍二:写操作要不要幂等

网络不可靠,超时是常态。客户端超时后会重试,而服务端可能已经执行成功。

如果接口不幂等,重试就会产生重复数据:重复评论、重复下单、重复扣款。

实现幂等的常见方式:

  • 客户端生成请求 ID,服务端去重表
  • 业务唯一键(同一用户对同一文章只能点一次赞)
  • 状态机 + 版本号,拒绝过期写入

四、取舍三:失败之后怎么补偿

分布式写往往跨多个存储:正文、索引、计数、搜索。任何一步都可能失败。

三种策略:

  1. 尽力而为:失败就记日志,人工修
  2. 重试 + 死信队列:自动重试,超过次数转人工
  3. 对账与重建:定期扫全量,重建派生数据

个人项目通常没资源做完整对账,那就至少做到“派生数据可重建”。索引坏了能从正文重建,计数错了能从明细重算——这比追求“永不失败”现实得多。

五、一个具体例子:索引的读改写

很多系统用“读索引 → 修改 → 写回”的方式维护列表。并发写时会互相覆盖。

可选方案:

  • 加锁(边缘环境通常没有)
  • 用支持原子追加的存储
  • 接受偶尔丢失,并提供重建
  • 把写收敛到单点(单区域、单实例)

没有哪个是免费的。关键是明确你选了哪个,并让系统在出错时可恢复。

结语

CAP 告诉你极端情况下必须放弃什么;PACELC 提醒你平时也在做取舍。真正让系统可靠的,往往不是选对了某个定理,而是幂等、重试、可重建这三件朴素的事。

0

评论 (0)

取消