CP vs AP 系统选型指南
2015 年,Netflix 宣布他们放弃了 ZooKeeper,转而使用自行研发的 Eureka 作为服务注册中心。消息一出,分布式系统社区炸开了锅。
有人嘲笑 ZooKeeper「过时了」,有人质疑「Eureka 的可用性真有那么重要吗」,还有人困惑:「ZooKeeper 不也能做服务发现吗?」
实际上,这场「争议」背后是一个经典的架构问题:你的系统应该选 CP 还是 AP?
这个问题的答案,不是「AP 更好」或「CP 更安全」那么简单。它取决于你的业务本质——是数据一致性优先,还是服务可用性优先。
一、CP 与 AP 的本质区别
CP 和 AP 是动态行为选择,不是静态标签
在深入讨论之前,我们需要先明确一个关键点:CP 和 AP 不是系统的静态标签,而是系统在分区时做出的行为选择。
一个系统在 99% 的时间里可能表现得像 AP(高可用、弱一致),但在关键的分区时刻,它必须明确地站在 C 或 A 的一边。
CP 系统:在分区时放弃可用性
CP 系统的核心特征是:宁可停止服务,也不返回不一致的数据。
当网络分区发生时,CP 系统会:
- 检测到分区
- 停止部分节点的响应(因为无法保证一致性)
- 在分区恢复后,进行数据同步
- 恢复服务
代表系统包括:ZooKeeper、etcd、HBase、MongoDB(副本集模式)、Redis Sentinel
AP 系统:在分区时放弃一致性
AP 系统的核心特征是:宁可返回旧数据,也要保证服务可用。
当网络分区发生时,AP 系统会:
- 检测到分区
- 让各分区独立继续服务
- 在分区恢复后,通过后台同步或冲突解决机制逐步恢复一致
- 可能产生「脑裂」——多个分区各自为政
代表系统包括:Eureka、Cassandra、DynamoDB、Redis Cluster
二、典型系统分析
ZooKeeper:CP 的教科书
ZooKeeper 是 CP 系统的典型代表。它的 ZAB 协议(ZooKeeper Atomic Broadcast)保证了:
- 强一致性:所有节点对状态的看法完全一致
- 原子性:写入要么在所有节点成功,要么失败
- 分区容忍:能够容忍网络故障
ZooKeeper 的可用性代价
但代价是:在 Leader 节点故障或网络分区期间,整个系统不可用。这就是为什么 ZooKeeper 适合做分布式协调服务(如分布式锁、配置管理),但不适合做需要高可用的服务发现——想象一下,当你的 Kubernetes Master 节点网络抖动时,整个集群的服务发现突然不可用……
当网络分区导致 Follower 与 Leader 失联时:
- Leader 所在的分区继续工作
- 失联的 Follower 被视为「不存在」
- 如果无法达成多数派写入,系统会停止服务
这就是为什么 ZooKeeper 适合做分布式协调服务(如分布式锁、配置管理),但不适合做需要高可用的服务发现——想象一下,当你的 Kubernetes Master 节点网络抖动时,整个集群的服务发现突然不可用……
Eureka:AP 的践行者
Netflix 设计 Eureka 的核心理念是:服务注册中心必须是永远可用的。
Eureka 的工作方式完全不同:
- 每个 Eureka Server 节点都是对等的,都可以接受服务注册
- 服务启动时向所有 Eureka Server 注册
- 节点之间通过心跳同步数据(但不保证强一致)
- 分区期间,每个分区独立继续工作
Eureka 的代价是:
- 可能出现「脏读」:消费者可能读到已下线服务的注册信息
- 数据不是强一致的:不同节点的注册表可能暂时不同
- 依赖客户端缓存:Eureka 客户端会缓存注册表,以应对 Server 不可用
Netflix 的可用性优先哲学
Netflix 认为,对于服务发现来说,短暂的「读到脏数据」比「服务完全不可用」要好得多。这个判断,基于他们自身的业务场景——Netflix 的服务需要 7×24 小时可用,任何全局性的服务不可用都会直接影响用户体验。
Redis:两种模式的集合
Redis 是一个有趣的案例:它同时支持 CP 和 AP 两种模式,取决于你使用哪种部署架构。
Redis Sentinel(CP):当主节点故障时,Sentinel 会通过 Raft 协议选举新主。在选举期间,整个集群不可写。一旦新主当选,数据会从旧主同步过来,然后恢复服务。
Redis Cluster(AP):每个分片都有主从节点,但分片之间是独立的。当某个分片的主节点故障时,该分片的从节点会升级为主节点。但其他分片继续正常服务。这就是 Redis Cluster 能够在部分分区时继续服务的原理。
三、权衡矩阵:如何选择
选 CP 还是 AP,不是非此即彼的选择题。它需要基于业务场景进行权衡。
核心权衡维度
选型决策树
场景化选型建议
四、混合方案:CP + AP 的实践
Netflix 的实践告诉我们:大型系统往往需要同时使用 CP 和 AP 系统,它们服务不同的业务需求。
Netflix 的混合架构
Netflix 的服务治理架构同时包含:
- Eureka(AP):服务注册与发现,保证服务在任何时候都可被发现
- Zuul/APIGateway:路由层,根据 Eureka 的信息进行负载均衡
- Hystrix/Resilience4j:熔断器,在服务不可用时快速失败
- Archaius:配置中心,使用 ZooKeeper(CP)保证配置一致性
这种设计的思路是:
- 用 AP 系统处理「让服务活着」的问题
- 用 CP 系统处理「让数据正确」的问题
- 用 熔断器在两者之间建立隔离层
混合选型的决策框架
五、真实案例:选型踩坑记
案例一:电商公司 ZooKeeper 选型失败
点击展开:电商公司 ZooKeeper 选型失败案例
某中型电商公司在 2018 年进行微服务改造,技术选型时认为 ZooKeeper「更成熟、更可靠」,于是用它做服务注册中心。
问题很快出现:
- 单点瓶颈:所有服务都连向 ZooKeeper 集群,在双十一大促期间,ZooKeeper 成为性能瓶颈
- 可用性事故:一次机房网络抖动导致 ZooKeeper 集群分区,大量服务实例被判定为「死亡」,服务发现完全不可用
- 扩容困难:需要为 ZooKeeper 分配独立的高配机器,成本高昂
最后这家公司不得不迁移到 Eureka,并在 Eureka 之上实现了服务隔离和熔断机制。
案例二:金融公司 Eureka 选型后的噩梦
点击展开:金融公司 Eureka 选型案例
另一家金融公司听说 Eureka「高可用」,于是用它做交易系统的服务发现。
问题是:金融交易对一致性有严格要求。当网络分区时,Eureka 可能让两个不同的服务实例同时认为自己是「主节点」,导致重复扣款。
解决方案:这家公司后来在 Eureka 之上实现了「双注册」机制:服务同时向 Eureka(AP)和 ZooKeeper(CP)注册,路由层只在 ZooKeeper 中发现「主节点」——这是一个典型的 CP + AP 混合方案。
六、反模式:同时要求强一致和强可用
「既 CP 又 AP」是认知陷阱
最后,让我们讨论一个最常见的错误认知:存在「既 CP 又 AP」的系统吗?
答案是:不存在。
如果你听到有人说「我们的系统既保证一致性,又保证可用性」,要么是:
- 他们对 CAP 理论理解有误
- 他们的系统不是真正的分布式系统(单机或假设永不分区)
- 他们说的「一致性」不是 CAP 中的强一致性(可能是最终一致性) :::
术语表
:::tip 结论:业务本质决定选型
回到文章开头的那个问题:Netflix 为什么放弃 ZooKeeper?
答案很简单:因为 ZooKeeper 是 CP 系统,而服务发现需要 AP 系统。Netflix 的业务场景决定了,他们宁可接受服务发现的短暂不一致,也不能容忍任何时刻的服务不可用。
你的业务场景呢?是选 CP 还是 AP?答案不在于「哪个更好」,而在于「哪个更符合你的业务本质」。