3. 分布式系统与共识:CAP定理、FLP不可能性、Paxos/Raft共识算法简介
分布式系统,说白了就是一群计算机商量着干活。但商量这件事,远比想象中难。
我早年参与过一个跨机房部署的项目,数据同步总是出问题。那时候我还没搞懂CAP定理,踩了不少坑。今天咱们就把分布式系统的几个核心理论掰开揉碎讲清楚。
3.1 CAP定理:分布式系统的“不可能三角”
CAP定理是分布式系统的入门第一课。它告诉我们:一个分布式系统,最多只能同时满足以下三个特性中的两个。
| 特性 | 含义 | 通俗理解 |
|---|---|---|
| C(Consistency)一致性 | 所有节点在同一时刻看到的数据相同 | 你写了一条数据,所有人立刻都能读到 |
| A(Availability)可用性 | 每个请求都能收到非错误的响应 | 系统一直在线,不会拒绝请求 |
| P(Partition Tolerance)分区容错性 | 系统在网络分区时仍能正常运行 | 网络断了,系统还能继续工作 |
核心结论:网络分区是必然发生的,所以P必须选。实际是在CP和AP之间做取舍。
为什么不能三者全要?我举个例子你就明白了。假设两个节点A和B,中间网络断了。这时候你往A写了一条数据,B读不到。如果B坚持返回最新数据(一致性),就得拒绝请求(牺牲可用性)。如果B继续返回旧数据(可用性),就牺牲了一致性。
我在做支付系统时,账务模块选了CP——宁可暂时不可用,也不能账对不上。而用户头像服务选了AP——头像旧一点没关系,但不能加载不出来。
我的建议:别纠结“选CP还是AP”,先想清楚业务场景。金融、库存选CP;社交、缓存选AP。
3.2 FLP不可能性:共识的“天花板”
CAP告诉你“不能全要”,FLP告诉你“更惨”——在异步网络里,哪怕只有一个节点可能崩溃,你也永远无法保证在有限时间内达成共识。
嗯,这句话有点绕。我拆开说:
- 异步网络:消息可能延迟,也可能丢失,没有超时机制
- 一个节点崩溃:你无法区分它是“慢”还是“挂了”
- 有限时间内:不存在一个确定性的算法,能在所有情况下保证共识
说白了,FLP不可能性给共识算法画了一条红线:别想找到“完美方案”,只能找“大概率能行”的方案。
避坑指南:我曾经看到有人试图设计一个“绝对可靠”的共识协议,折腾了半年发现FLP已经证明不可能。别在理论上钻牛角尖,工程上我们接受概率性成功。
那实际系统怎么绕过FLP?常见做法是引入随机性(比如Paxos的随机超时)或部分同步假设(比如Raft的Leader选举)。
3.3 Paxos:共识算法的“祖师爷”
Paxos是Lamport大神在1990年提出的,公认最难懂的算法之一。但它的核心思想其实很简单:
- 提议者(Proposer):提出一个值
- 接受者(Acceptor):投票决定是否接受
- 学习者(Learner):学习最终结果
Paxos的核心是“两阶段提交”:
- Prepare阶段:提议者问所有接受者“我能不能提一个值?”接受者回复“可以,但我已经承诺了某个编号更高的提议”
- Accept阶段:提议者收到多数派回复后,正式提交值
关键约束:一旦接受者承诺了编号N的提议,就不能再接受编号小于N的提议。这保证了“一旦达成共识,就不会被推翻”。
我在做分布式配置中心时,底层用的就是Multi-Paxos。说实话,实现起来比理解难得多。光是处理“提议编号冲突”就让我调试了两周。
3.4 Raft:更“工程化”的共识算法
Raft是2013年提出的,目标就是“让共识算法变得可理解”。它把Paxos的复杂逻辑拆成了三个子问题:
| 子问题 | Raft的解法 |
|---|---|
| Leader选举 | 节点随机超时,先到先得 |
| 日志复制 | Leader统一写日志,Follower同步 |
| 安全性 | 只有拥有最新日志的节点才能成为Leader |
Raft的选举过程很直观:
- 所有节点初始都是Follower
- 如果Follower在超时时间内没收到Leader心跳,就变成Candidate
- Candidate给自己投票,并请求其他节点投票
- 获得多数票的节点成为Leader
个人经验:Raft的随机超时范围很关键。我习惯设150-300ms,太短容易频繁选举,太长影响故障恢复速度。
日志复制的过程更简单:Leader收到客户端请求后,追加到自己的日志里,然后并行发给所有Follower。等多数Follower确认写入后,Leader才提交这条日志。
// Raft日志复制伪代码
func (leader *Node) ReplicateLog(entry LogEntry) {
leader.log.append(entry)
for _, follower := range leader.followers {
go func(f *Node) {
f.AppendEntries(leader.log[leader.nextIndex[f.id]:])
}(follower)
}
// 等待多数派确认
if leader.majorityCommitted() {
leader.commitIndex = leader.log.length()
leader.applyToStateMachine(entry)
}
}
3.5 共识算法对比:选型指南
实际项目中怎么选?我整理了一张对比表:
| 维度 | Paxos | Raft | Zab(ZooKeeper) |
|---|---|---|---|
| 理解难度 | 高 | 低 | 中 |
| 实现复杂度 | 高 | 中 | 中 |
| Leader角色 | 可选 | 固定 | 固定 |
| 典型应用 | Google Chubby | etcd, Consul | ZooKeeper |
| 社区生态 | 少 | 丰富 | 成熟 |
我的建议:新项目优先选Raft。Paxos虽然理论优雅,但实现坑太多。除非你有Lamport那样的数学功底,否则别自己造Paxos轮子。
下面这张图展示了分布式系统共识的核心知识体系:
最后说一句:理论是地图,实践是走路。CAP和FLP告诉你边界在哪,Paxos和Raft告诉你路怎么走。但真正落地时,网络延迟、节点故障、数据一致性这些坑,一个都少不了。
我当年第一次部署Raft集群时,三个节点在同一个机房都能出问题——时钟漂移导致频繁选举。后来加了NTP同步和选举限流才稳定下来。嗯,这些实战经验,后面章节会详细展开。