分布式系统填坑记:被 CAP 定理按在地上摩擦的血泪史

前言:只要加机器就能解决性能问题?

刚入行的时候,我以为分布式系统就是“一台机器扛不住,就加两台”。
直到又一次,生产环境的 Redis 集群即使有 3 个副本,依然读到了旧数据,导致用户账户余额显示错误,差点引发资损。

排查了一整夜,最后发现是网络分区导致的数据不一致。
那一刻,CAP 定理 像一块砖头一样狠狠地砸在了我的脸上的。

这篇文章不是教科书式的理论分析,而是我在分布式泥潭里摸爬滚打出来的血泪经验。

1. CAP 定理:不可能三角

Eric Brewer 告诉我们,在一个分布式处理数据系统中,不可能同时满足:

  • C (Consistency, 一致性): 每次读取都能读到最新的写入。(不是事务的一致性,而是线性一致性)。
  • A (Availability, 可用性): 每个请求都能在有限时间内得到非错的响应。(哪怕数据是旧的)。
  • P (Partition Tolerance, 分区容错性): 允许网络丢包、断连。

因为网络肯定会断(光缆被挖断,交换机重启),所以 P 是必须满足的
剩下的就是 CP vs AP 的抉择。

血淋淋的案例:Redis vs Zookeeper

  • Zookeeper 是 CP 系统
    当主节点挂掉,选主期间(几秒到几十秒),整个集群拒绝服务。它宁可不干活,也不能告诉你错误的信息。这适合做服务发现、分布式锁。

  • Redis (主从+Sentinel) 倾向于 AP
    Master 挂了,Slave 还没同步完最新的数据。Sentinel 提升 Slave 为新 Master。此时 client 读到的数据就丢失了刚才那几毫秒的写入。但服务一直可用。

我的那个余额 bug,就是因为我们把 Redis 当作强一致性数据库来用了。

2. 强一致性的代价:Paxos 与 Raft

为了追求一致性,我们引入了共识算法。最著名的其实是 Raft(Etcd, TiKV 都在用)。

Raft 的核心是 Look-on-Majority(少数服从多数)。

  1. Leader Election: 大家都投我,我就是老大。
  2. Log Replication: 我收到写入请求,先记小本本(Log),然后群发给 Follower。
  3. Commit: 收到超过半数 Follower 的 “OK”,我才告诉 Client “写成功了”。

这个过程虽然安全,但是
每次写入都要跨网络往返 RTT。如果你的节点跨机房(比如北京-上海),光速限制了你的 TPS 上限。

脑裂 (Split Brain)

最可怕的场景:网络把机房切成两半。

  • A区:3个节点。
  • B区:2个节点。

Raft 很聪明,A区能凑齐多数,继续选主工作;B区凑不齐,自动停止写服务。
但如果你用的自研的简单主从复制(MySQL 主主同步),没有 Arbiter,可能两边都觉得自己是 Master,都在接受写入。等网络恢复时,数据就彻底乱了(冲突)。

3. 最终一致性 (Eventual Consistency)

鉴于强一致性太贵,互联网公司大都选择了妥协:BASE 理论
Basically Available, Soft state, Eventual consistency.

典型的代表是 Dynamo (Amazon) 和 Cassandra
它们引入了 Quorum 机制:$N, W, R$。

  • $N$: 副本数 (3)
  • $W$: 写入多少个算成功 (1) -> 极速写入,但极不安全。
  • $R$: 读取多少个算成功 (1)

如果我们设置 $W=1, R=1$,那就是极致的 AP。
如果我们设置 $W + R > N$(比如 $W=2, R=2$),根据鸽巢原理,读取的副本里一定包含最新的那个写入。这就是强一致性了吗?

不!还有时钟问题

4. 最大的坑:分布式时钟

在单机上,System.currentTimeMillis() 是可信的。
在分布式系统里,时间是不可信的
机器 A 和机器 B 的时间可能差几百毫秒。Google 的 TrueTime API 用原子钟和卫星才把误差压到 7ms。

当你在 Cassandra 里用 Last-Write-Wins (LWW) 策略解决冲突时,那个“Last”到底是谁的 Last?
如果一个旧数据的机器时间快了 1 秒,它可能会覆盖掉新数据!

Vector Clock (向量时钟) 是解决方案,但它带来了存储成本的膨胀。

5. 总结:如何在泥潭中求生

在设计分布式系统时,永远要问自己三个问题:

  1. 数据丢了能不能接受? (不能 -> CP / Raft)
  2. 服务停几秒能不能接受? (不能 -> AP / Gossip)
  3. 如果网络断了,我的系统会变成什么样? (脑裂?降级?)

没有完美的架构,只有最适合业务场景的权衡 (Trade-off)。
如果你的业务只是统计点赞数,请毫不犹豫地拥抱 AP,丢几个赞没人会在意。
如果你的业务是银行转账,请死守 CP,哪怕系统因为网络抖动不可用,也不能算错一分钱。

把复杂性留给中间件(Etcd, ZooKeeper),把简单留给业务逻辑。 这就是我填了无数坑之后的所谓“智慧”。


分布式系统填坑记:被 CAP 定理按在地上摩擦的血泪史
https://www.qixyuan.top/2025/07/25/8-distributed-pitfalls/
作者
QixYuan
发布于
2025年7月25日
许可协议