CAP定理
CAP 定理(CAP Theorem),又被称作布鲁尔定理(Brewer’s Theorem),是分布式计算领域的“宪法”。它指出,对于一个分布式计算系统,不可能同时满足以下三点,最多只能同时满足其中的两点:
1. CAP 的三个指标
-
C - Consistency (一致性)
- 定义: 每次读取要么获得最近写入的数据,要么获得错误(Every read receives the most recent write or an error)。
- 通俗理解: “强一致性”。当你向主数据库写入
x=1后,下一秒无论从集群中的哪个节点读取x,都必须读到1。如果做不到,系统就应该拒绝读取,而不是返回旧数据。
-
A - Availability (可用性)
- 定义: 每次请求都能获得非错误的响应,但不保证返回的是最新数据(Every request receives a (non-error) response, without the guarantee that it contains the most recent write)。
- 通俗理解: “服务永远在线”。不管系统内部是否同步完成,只要节点没挂,就必须给用户返回数据(哪怕是几秒前的旧数据)。
-
P - Partition Tolerance (分区容错性)
- 定义: 尽管网络丢包或节点间网络中断(网络分区),系统仍能继续运行。
- 通俗理解: “断网也不死”。在分布式系统中,网线被挖断、交换机故障是常态。P 是分布式系统的基础,通常是不可妥协的。
2. 为什么只能“三选二”?
由于网络分区(P)在分布式系统中是客观存在的必然(你无法保证网络 100% 连通),所以真正的选择题其实只有两种:CP 或 AP。
我们可以想象这样一个场景:你有两个数据库节点(节点 A 和 节点 B),它们之间的网线断了(发生了分区 P)。此时用户向节点 A 写入了新数据。
-
选择 CP (放弃 A): 为了保证 B 节点的数据和 A 一致,A 必须锁定或等待 B 确认。但因为网线断了,A 永远等不到 B 的确认。为了不破坏一致性,系统必须报错或阻塞,告诉用户“现在服务不可用”。
- 典型应用: 银行转账系统、分布式锁(如 ZooKeeper、Etcd)。
-
选择 AP (放弃 C): 为了保证服务可用,节点 A 允许写入,并告诉用户“成功了”。但此时节点 B 还是旧数据。系统保证了服务没挂,但牺牲了数据的一致性(此时 A 和 B 的数据不一致了)。
- 典型应用: 社交网络点赞(晚几秒看到也不要紧)、DNS 系统、大多数 Web 缓存。
-
CA (放弃 P): 这意味着系统不允许网络出问题。这在分布式系统中是不现实的。只有单机数据库(如单机版 MySQL)才属于 CA 系统。
3. 进阶:PACELC 定理
随着技术发展,人们发现 CAP 过于简化了。因为网络并不会天天断(大部分时间 P 是不发生的)。于是有了更精细的 PACELC 定理:
如果有分区(P),则在 A 和 C 之间选; 否则(Else,正常运行时),在 L(Latency,延迟)和 C(Consistency,一致性)之间选。
这意味着,即使网络没断,为了追求极致的性能(低延迟),我们也往往会牺牲强一致性(比如使用异步复制)。
典型技术理解
- Redis:
- 默认模式: 它是 AP 倾向的。主从复制是异步的,主库写完立刻返回,不等待从库。如果主库挂了,从库上位,可能会丢失刚刚写入的数据。
- Redlock (分布式锁): 当你试图用 Redis 做分布式锁时,你实际上是在强行构造一个 CP 场景(要求多个节点确认才能锁成功),这在网络不稳定时会很棘手。
- MongoDB:
- 它是一个可以在 CP 和 AP 之间切换的数据库。
- 如果你设置
writeConcern: "majority"(写入必须同步到大多数节点才算成功),你就在追求 CP。 - 如果你设置
readPreference: "nearest"(读取最近的节点,不管是不是旧数据),你就在追求 AP