首页>安全>正文

CAP不是三选二:积分、MQ、注册中心的真实取舍

Feng 7 阅读 安全

CAP定理被讲烂了,但落到代码和故障现场,它其实是一把尺子:量的是业务能容忍什么、必须保住什么。

早年在电商订单系统上吃过亏。为了强一致硬上两阶段提交,一次网络抖动就把整条链路拖垮,服务雪崩。那次之后才明白,问题不在定理本身,在于没把一致性拆成可量化的粒度。

分区容错不是选项,是前提

只要系统跨网络,断线、交换机故障、机架掉电都是常态。所以P(分区容忍)基本是默认要保的,真正的取舍永远发生在C和A之间。

但别把它理解成固定套餐。一致性有强弱之分,可用性也有程度差异。最终一致性本身也是一种一致性,只是用延迟换来了更高的可用性。

积分服务:想选CP,最后落到的却是可用性

团队重构过一个用户积分服务,每天几百万笔交易。最怕两件事:积分扣了没到账,或者同一张券被领两次。业务方原话是数据绝对不能错。

按教科书思路,这该走CP:ZooKeeper、etcd 都是典型CP实现,写入后所有节点视图一致。代价是分区发生时,被隔离的少数节点直接停止服务。

这对积分场景是致命的。用户可能正好在分区窗口里用不了积分,投诉量立刻上来。

最后方案是Raft选主,写操作只走Leader,叠加本地事务和幂等设计。分区期间靠超时重试和降级兜底,把可用性损失压到最小。

配图

这里有个坑:幂等键的生成必须和业务主键绑定,不要用请求ID。请求ID在重试链路上会变,去重直接失效。

消息队列:AP底座上叠最终一致

订单异步通知最早用RabbitMQ,默认AP风格,高可用但允许短暂不一致。

有次下游系统挂掉,消息堆积,重试后顺序错乱,结算对不上账。后来换成Kafka,配合消费者幂等,关键消息标记为至少一次投递再加去重。

本质是在AP底座上补了一层最终一致性保障。实际系统很少是纯CP或纯AP,更多是靠中间件分层组合出不同的一致性级别。

注册中心:Eureka和Consul怎么选

Eureka是AP,一半节点挂掉,其余节点照样能注册和发现服务。代价是服务下线后几秒内仍可能被调用到脏数据。

Consul是CP,强一致,但分区时整个注册中心拒绝写操作。

我的判断:小规模团队用Consul更省心,服务少、分区概率低,强一致带来的心智负担小。大型互联网公司业务成千上万,宁可容忍偶尔调错一次,也不接受注册中心整体不可用。

同一套分布式系统里,不同组件可以有完全不同的CAP倾向,这很正常。

分布式事务不是死路

一听到CAP就断言分布式事务没戏,是误解。TCC和Saga本质就是在一致性和可用性之间做折中:允许短暂不一致,靠补偿机制最终收敛。

我们用Seata做过跨库转账。第一步扣款,第二步加款,第二步超时就走回滚冲正。期间用户可能看到余额暂时没变,但最终结果是对的。

放弃强一致换高可用,用业务补偿兜底,这是CAP在工程里最常见的落地形态。

一个容易踩的验证盲区

很多人测分区容错只测网络断开,不测时钟漂移。Raft、ZooKeeper 这类协议对时钟依赖不高,但依赖超时选举,节点间时钟漂移过大时会引发频繁重新选主。

实操里用 NTP 同步各节点时间,偏差控制在百毫秒级以内,再配合 chaos 工具注入网络延迟和丢包,观察选主频率和请求成功率。判定标准很简单:分区恢复后,数据能收敛到一致状态,且期间没有出现脑裂写入。

别一上来就多副本同步

见过不少团队,架构评审第一句话就是上多副本强同步。结果延迟高得离谱,写入路径被拖死。

大多数业务场景根本不需要强一致。朋友圈点赞延迟几秒显示没人管,但支付扣款少一分钱就是事故。

把业务对一致性和可用性的容忍度量化出来,再用技术手段把损失做小。CAP不是选择题,是设计指南。

Feng
这位作者很神秘,还没有填写简介。