Skip to content

分布式系统总览

绝大多数工程师第一次碰分布式,是被业务推着上的——单库扛不住、单机内存装不下、机房挂了一次。真正难的不是"加机器",而是加完机器之后,你发现这个系统跟你以前写过的所有系统都不一样:它会"部分坏掉"(一半节点活着一半死了)、它的时间是错的、它的网络会骗你、它的状态散在一堆机器里没人能一眼看完。这一篇先把这条不归路上的全貌看清楚——为什么要上分布式、上完要付什么代价、什么时候根本不该上,以及后续 29 篇要展开的"分布式难题"到底有哪几类。

一句话先记住:分布式系统的本质不是"多机协作",而是"在部分失败的世界里维持不变量"——这句话理解了,后面的 CAP / FLP / Paxos / 2PC 全都有了着力点。单机的故障模型是"全活或全死",分布式的故障模型是"一部分活一部分死,而你不知道是哪一部分"——95% 的诡异 bug 都源自工程师没把这件事放在心上。


一、为什么需要分布式

绝大多数系统第一天都是单机的——一台机器、一个进程、一份数据。单机系统最大的优势是"简单":没有网络、没有部分失败、没有时钟漂移、没有协调开销。能在单机上跑就在单机上跑,这不是落后,这是工程纪律

那为什么后来还是上了分布式?只有四个真实理由——其余都是借口。

1.1 单机硬件的物理天花板

维度单机上限(2025 年商用配置)真实业务很快就破
CPU单机最多 ~256 核,~8 GHz 频率撞天花板十年了实时计算、视频转码
内存单机最多 ~12 TB DDR5(主板槽位限制)大模型推理、图数据库
磁盘单盘 ~30 TB NVMe,单机 ~500 TB(机箱槽位)日志、视频、原始数据
网络单机 ~400 Gbps 网卡,~100 万 PPSCDN、IM 长连接
持久化吞吐单 SSD ~7 GB/s 写,单机 ~30 GB/s海量写入业务

注意:单 CPU 频率十年没涨——摩尔定律对单核早就死了,性能只能横向加机器。这一条比"内存装不下"更根本——就算你买得起更贵的机器,主频也涨不上去

1.2 容错:任何一台机器都会挂

单机系统的可用性上限就是单机硬件的可用性——典型服务器 MTBF(平均无故障时间)是 3-5 年,机房环境下年故障率约 2-5%(磁盘、电源、内存条、网卡都可能挂)。

单机可用性:99% (一年挂 87 小时)
两副本     :99.99% (一年挂 52 分钟)
三副本     :99.9999% (一年挂 30 秒)

注意这是「理想模型」——实际副本不独立(同机房断电、同版本 bug、同操作员误操作),多副本的真实可用性远低于数学期望。但即便如此,冗余是绕不开的——单机系统不可能达到 99.9% 以上。

1.3 地理分布:用户在全球,光速救不了你

光在光纤里跑 1000 公里需要 5 ms,北京到伦敦单程 ~70 ms,往返 ~140 ms。如果你的服务只部署在北京:

  • 北京用户:RTT 几毫秒,体验丝滑
  • 伦敦用户:每次请求 140 ms 起步,点一下页面感觉电脑卡了

光速是物理常数,改不了——只能"把服务挪到用户附近"。这就要求:

  • 多机房部署(全球 CDN、边缘节点)
  • 跨地域数据复制(美国和中国都有一份数据)
  • 跨地域一致性协议(怎么让两份数据保持一致)

跨地域分布式系统的难度比单机房高一个数量级——单机房 RTT < 1 ms,跨地域 RTT 几十到几百 ms,这意味着同步协议(Paxos / Raft)的延迟成本完全不同。Spanner 给你跨地域强一致,代价是每次写入 ~100 ms 延迟。

1.4 弹性:流量不是恒定的

互联网业务的流量峰谷比例常常是 10:1 到 100:1——双 11、春晚、突发热点。

日常 QPS: 1 万
峰值 QPS: 100 万

按峰值买单机 → 平时 99% 的算力浪费 → 成本爆炸
按日常买单机 → 峰值挂掉 → 收入归零

按日常买基础容量 + 弹性扩容 → 平时省钱、峰值能扛 → 唯一可行解

单机系统没有弹性——你不可能 11 月 1 日插一根内存条,11 月 12 日拔下来。分布式的"加机器" / "缩容"才是弹性的物质基础


二、分布式的真实代价

上了分布式不是"加几台机器"这么轻松,你付出的代价远比想象中大。这一节把代价摊开——看完心里有数,选型时才不会一拍脑袋上分布式。

2.1 延迟从纳秒涨到毫秒

单机内存访问:      ~100 ns
单机 SSD 访问:     ~100 μs (1000 倍)
同机房网络往返:   ~500 μs (5000 倍)
跨机房网络往返:   ~10 ms  (10 万倍)
跨地域网络往返:   ~100 ms (100 万倍)

只要请求一跨进程,延迟就涨 1000 倍以上。原本单机一次函数调用 100 ns,分到两台机器变成 500 μs——业务代码看起来一样,延迟相差 5000 倍

更可怕的是长尾:同机房 RTT 平均 500 μs,P99 可能 5 ms,P999 可能 50 ms——一次业务请求调用 20 个下游服务,P999 长尾叠加起来直接超时。

2.2 调试复杂度爆炸

单机 bug:开 gdb 单步,改完编译再跑。 分布式 bug:

  • 复现难:涉及多节点状态、网络时序、并发顺序,复现一次 bug 可能要重放上百万条消息
  • 观测难:日志散在 N 台机器上,没有统一的"系统当前状态"视图
  • 推理难:happens-before 关系全乱(详见 06 篇 Lamport 时钟),"哪个操作先发生"这种单机的常识在分布式里根本说不清

Jepsen(知名分布式系统测试项目)发现的几乎所有 bug,单元测试都测不出来——只有在网络抖动 + 节点宕机 + 并发请求三重组合下才会暴露。分布式 bug 是"罕见组合"的 bug,常规测试覆盖不到。

2.3 运维成本指数级上升

维度单机分布式(10 节点)
部署一次 SSH配置管理工具 / K8s
监控看一台机器的 CPUPrometheus + 服务网格 + 链路追踪
故障定位一台机器多节点日志聚合 + 分布式追踪
数据备份一份磁盘多副本一致性、备份策略、恢复演练
升级停机重启滚动升级、灰度发布、回滚预案
安全一道防火墙服务间 mTLS、零信任网络

人力成本:维护一个 100 节点的分布式系统,至少需要 2-3 个专职 SRE——而单机系统一个开发者业余维护就够了。

2.4 CAP 给你的硬约束

CAP 定理(详见 04 篇)说:分区发生时,一致性 C 和可用性 A 只能二选一

  • 选 C:网络分区时拒绝服务(ZooKeeper / etcd 的选择)
  • 选 A:网络分区时允许不一致(Cassandra / DynamoDB 的选择)

这不是技术问题,是物理问题——网络永远会分区,你必须选一边。单机系统没这个困扰:要么全活要么全死,不存在"一半活一半死、互相通信不上"的状态。

2.5 状态分散,事务消失

单机数据库的 ACID 事务是免费的——BEGIN; UPDATE A; UPDATE B; COMMIT;,两个更新原子完成。

分布式下,A 在节点 1、B 在节点 2,你想让它们原子提交?恭喜你进入分布式事务的深坑(详见 20-24 篇):

  • 2PC:有协调者宕机风险,会卡死
  • Saga:补偿事务,代价是放弃隔离性
  • TCC:业务侵入,每个操作要写三遍
  • Percolator / Spanner:重量级,延迟高

没有"完美的分布式事务"——所有方案都是在 ACID 的某个字母上妥协


三、什么时候不该上分布式

工程师最大的虚荣心之一就是"造大系统"——但真实世界里,90% 的业务用单机 + 主从就够了。这一节专门讲"什么时候不该上分布式"——能不上就不上,是工程纪律,不是技术水平不够。

3.1 规模没到

指标单机能扛的上限真上分布式的合理时机
QPS~5 万 (简单接口)持续 > 10 万
数据量~1 TB (热数据)持续增长 > 10 TB
用户数~100 万 DAU> 500 万 DAU
团队规模3-5 个开发> 10 个开发

90% 的初创公司、内部工具、企业级应用,规模根本到不了上分布式的临界点。强行上分布式 = 用 10 倍的复杂度换 0 倍的好处。

3.2 单机能搞定的"伪需求"

  • "我们要做高可用":一主一备 + Keepalived + VIP 漂移就够了,不需要 Raft 集群
  • "我们要做读扩展":MySQL 一主三从就够了,不需要 Cassandra
  • "我们要做容灾":每天凌晨备份到 S3 + 异地冷备,不需要跨地域强一致
  • "我们要解耦":进程内事件总线 / 函数回调就够了,不需要 Kafka

判定标准:如果你能用「单机 + 主从 + 备份」解决,就别上分布式中间件。ZooKeeper / etcd / Kafka / Cassandra 这些重武器,带来的运维成本超过 90% 业务的承受能力

3.3 团队没准备好

分布式系统需要团队具备:

  • 混沌工程能力:能主动断网、kill 节点验证容错
  • 可观测性建设:链路追踪、指标体系、日志聚合
  • 运维自动化:K8s / Terraform / Ansible
  • SRE 文化:错误预算、SLO/SLI、故障复盘

这些能力建立至少需要 6-12 个月。团队没建好就上分布式 = 给自己埋雷,线上事故频率会上升一个数量级

3.4 业务对延迟极敏感

某些业务单机延迟才达标,分布式必然超时:

  • 高频量化交易:单笔决策要求 < 100 μs,任何网络跨进程都不可接受
  • 游戏服务器内逻辑:同区玩家交互要求 < 10 ms,强行分布式延迟翻 10 倍
  • 嵌入式 / 工业控制:实时性要求亚毫秒级

这类系统的"扩展"靠纵向(更强的单机)+ 业务分片(不同区不同进程),而不是"集群"


四、分布式系统的标志性问题

分布式系统的"难",集中在五类问题上。这五类后面 29 篇会一一展开,这里先把全貌看清楚——95% 的分布式 bug 最终都归到这五类之一的不变量被破坏

┌────────────────────────────────────────┐
│       分布式的五座大山                  │
├────────────────────────────────────────┤
│ 1. Partial Failure  (部分失败)         │
│ 2. Unreliable Network (网络不可靠)     │
│ 3. Concurrency      (并发)             │
│ 4. No Global Clock  (时间无全局)       │
│ 5. Distributed State (状态分散)        │
└────────────────────────────────────────┘

4.1 Partial Failure(部分失败)

单机系统:要么全活要么全死——进程崩了所有功能都没了,这反而是个"好"的故障模式,因为状态干净。

分布式系统:节点 A 活、节点 B 死、节点 C 半死(网络通但磁盘满)。这才是真实世界——你处理的不是"挂了",而是"半挂了"

单机故障:
   进程        进程
    ●    →     X
   (全活)    (全死)

分布式故障:
   N1 N2 N3 N4
   ●  ●  ●  ●     全活
   ●  X  ●  ●     一个挂(可能 N1 还以为 N2 活着)
   ●  ▲  ●  ●     一个半死(响应慢、超时但不返回错误)
   ●  ●  ●  ●     全活但 N1-N4 之间网络断了(脑裂)

部分失败是分布式最难处理的——因为你没法区分"对方挂了"和"网络通信慢"。这就是后面会反复出现的「network partition vs slow node」问题——同一个超时,可能是对方死了,可能是网延迟,你无法区分

4.2 Unreliable Network(网络不可靠)

互联网建立在一个事实上:TCP 给你可靠的字节流,但 TCP 之下的物理网络是不可靠的

  • 丢包:路由器拥塞 → 数据包丢 → TCP 重传,看起来"慢"
  • 乱序:多路径路由 → 包到达顺序变了 → TCP 重排
  • 重复:NAT 设备超时重发 → 同一个包到达两次
  • 延迟:跨地域、跨运营商、跨防火墙,延迟不可预测
  • 分区:交换机故障、防火墙规则错、BGP 路由抖动 → 整段网络隔离

这一切都是常态,不是异常——AWS / Azure / 阿里云每天都会发生几次"网络抖动",生产系统必须假设网络永远不可靠。详见 02 篇八大谬误。

4.3 Concurrency(并发)

单机并发:加锁就行(互斥量、读写锁、原子操作)。

分布式并发:两个客户端同时往同一个 key 写,谁先谁后?

  • 时间戳?两台机器的时钟不同步(详见 05 篇)
  • 加锁?怎么锁?在哪台机器上锁?(详见 26 篇分布式锁)
  • 仲裁?谁是仲裁者?仲裁者挂了怎么办?(详见 09-15 篇共识)
客户端 A → 节点 1 (写 x=1, t=10:00:00.123)
客户端 B → 节点 2 (写 x=2, t=10:00:00.122)

按时间戳:B 先 (t=10:00:00.122 < t=10:00:00.123)
但节点 2 的时钟比节点 1 慢 100 ms!
真实顺序:A 先

→ 用 wall clock 排序,可能把因果颠倒

4.4 No Global Clock(没有全局时钟)

单机系统:有一个唯一的、单调递增的时间——CPU 时钟或操作系统时间。

分布式系统:N 台机器有 N 个时钟,而且都不准

  • NTP 同步精度:几毫秒到几十毫秒,根本不够给数据库做事务排序
  • 跨地域 NTP:网络延迟波动大,可能差秒级
  • 闰秒、夏令时、时钟漂移:时间甚至会倒流

没有全局时钟意味着:

  • 你不能用 wall clock 排序两个事件
  • 你不能基于绝对时间设置 TTL("过期"概念变模糊)
  • 你不能简单判断"哪个事务先开始"

这就是为什么有 Lamport 时钟、向量时钟、HLC、TrueTime 这一连串发明(详见 05-08 篇)——没有完美的解,只有不同程度的近似

4.5 Distributed State(状态分散)

单机:一个进程的状态在一个进程里,内存 dump 一下就能看完。

分布式:状态散在 N 台机器,而且每台的副本可能不一样

  • 节点 1 看到的状态:x=1
  • 节点 2 看到的状态:x=2(还没同步)
  • 节点 3 看到的状态:x=3(本地缓存)

没有任何一个时刻,某个观察者能"看到系统的真实状态"——它要么看到旧的(还没同步)、要么看到部分的(只能拿到几个节点的)、要么看到不一致的(节点之间相互冲突)。

这是 CAP / 一致性模型 / 复制协议存在的根本原因——它们都在试图回答"在状态分散的世界里,我能给应用提供什么样的'看起来像单机'的契约"。详见 16-19 篇一致性模型。


五、本系列六层结构对应五座大山

写作计划里讲了本系列六层——这一节快速把"六层"和"五座大山"对应起来,你后续读每一篇都知道它在解决哪类问题。

┌─────────────────────────────────────────────────────────────┐
│ 层级           核心解决的山               关键概念            │
├─────────────────────────────────────────────────────────────┤
│ 心智 + 基础    全貌、词汇、约束           CAP / FLP / 故障模型│
│ (01-04)        全部五座山的入门             八大谬误           │
│                                                              │
│ 时间与因果     山 #4 (无全局时钟)          Lamport / HLC      │
│ (05-08)                                    Vector Clock      │
│                                                              │
│ 共识与复制     山 #1+#2 (部分失败、网络)   Paxos / Raft / ZAB │
│ (09-15)        让多机对一个值达成一致      FLP / 拜占庭       │
│                                                              │
│ 一致性模型     山 #5 (状态分散)            Linearizability   │
│ (16-19)        多副本的"看起来像单机"契约  Causal / CRDT     │
│                                                              │
│ 分布式事务     山 #3 (并发)                2PC / Saga / TCC  │
│ (20-24)        跨机操作的原子性            Percolator / Spanner│
│                                                              │
│ 工程基石       综合解决五座山的工程零件    Gossip / 选主      │
│ (25-30)                                    分布式锁 / 服务发现 │
└─────────────────────────────────────────────────────────────┘

读者画像里说,这个系列假设你已经用过至少一个分布式中间件——你在 Redis / Kafka / ZK / etcd 上踩过的坑,就是这五座山在你头上砸下来的具体形态。后面 29 篇是把这些坑系统化:告诉你坑长什么样、为什么不可避免、工程上怎么妥协绕过、什么时候妥协会失效


六、一个简单的"分布式 KV"思想实验

最后用一个思想实验,把这一篇的所有概念串起来。假设你要写一个"分布式 KV 存储"——支持 put(k, v)get(k),数据存在 3 台机器上。听起来很简单,对吧?

6.1 第一版:全写全读

python
def put(k, v):
    for node in [n1, n2, n3]:
        node.set(k, v)

def get(k):
    return n1.get(k)

问题:

  • put 时 n2 挂了,n2 没收到这次写。n1、n3 有新值,n2 有旧值——山 #1 部分失败
  • 两个客户端并发 put(k, v1)put(k, v2),n1 收到 v1 在前、n2 收到 v2 在前——最终 n1 是 v1,n2 是 v2,数据分裂——山 #3 并发 + 山 #4 时钟

6.2 第二版:多数派写

python
def put(k, v):
    ok = 0
    for node in [n1, n2, n3]:
        if node.set(k, v):
            ok += 1
    return ok >= 2  # 多数派成功

def get(k):
    return [n.get(k) for n in [n1, n2, n3]]  # 拿三份,看多数

问题:

  • 客户端 A put(k, v1),n1+n2 成功,返回 success
  • 客户端 B 同时 put(k, v2),n2+n3 成功,返回 success
  • n1=v1, n2=??, n3=v2——n2 看到了两个并发写,谁覆盖谁?山 #3 并发
  • 用时间戳?两台客户端时钟不同步——山 #4 时钟

6.3 第三版:加共识协议

引入 Raft——所有写都通过 Leader,Leader 按日志顺序复制到多数派:

python
def put(k, v):
    leader.append_log(SET, k, v)  # Raft 复制
    # Leader 等待多数派确认后 commit

def get(k):
    return leader.read(k)  # 强一致读

这就是 etcd / Consul / TiKV 的核心——共识协议负责把"分布式状态"伪装成"单机状态"

但是代价:

  • Leader 挂了要选举,选举期间不可用(几秒)——山 #1
  • 每次写都要等多数派确认,延迟 = max(到各节点的 RTT)——山 #2
  • 跨地域部署时,延迟可能 100 ms+——山 #2

这就是后面 09-15 篇要展开的全部内容——共识协议是"分布式 KV"能正确工作的根基。看完那一层,你再回头看 etcd / ZK / TiKV 的设计,就能直接看穿。


七、几个常被忽略的反直觉事实

7.1 "无状态服务"是营销话术

真正的"无状态"几乎不存在——至少有日志、缓存、配置、连接池这些隐式状态。所谓"无状态服务",只是把状态推给了下游存储最终某个地方必须有人扛状态,而扛状态那一层才是分布式系统真正难的地方

7.2 节点越多,系统越脆弱(不是越健壮)

直觉:加机器 = 更可靠。 现实:加机器 = 更多故障点 + 更多协调开销。

节点数 N 越大,任意时刻有节点挂的概率越高——单机 99% 可用,10 个节点全活的概率 ≈ 90%,100 个节点全活的概率 ≈ 37%。分布式系统的"健壮"是靠副本和重试达成的,不是靠"机器多"

7.3 异步 ≠ 解耦

很多人以为"上了 Kafka 就解耦了"——。Kafka 只是把"同步调用失败"换成了"异步消息积压",问题没消失,只是变形消息丢了、消费者挂了、积压爆了——每个都是新坑。

7.4 "我们用的是 X,所以不会有 Y 问题"

最常见的认知错误。Kafka 用了不代表你不会丢消息(配置错了 acks=1 就会丢),ZK 用了不代表你不会脑裂(客户端 session 过期处理不对就会脑裂)。工具不能替代理解——这就是这个系列要写 30 篇的原因。


八、读完这一篇后该有的认知

  1. 分布式不是"加机器",是"换一种思维方式"——故障模型、时间模型、状态模型全变了
  2. 能不上分布式就不上——单机 + 主从能撑住 90% 的业务,运维成本是分布式的 1/10
  3. 真上分布式,准备好为"部分失败 + 网络不可靠 + 并发 + 无全局时钟 + 状态分散"五座大山付代价
  4. 每个分布式中间件都在解决五座山中的一部分——后续 29 篇会把每个工具拆开看
  5. 理论不是装饰品——CAP / FLP / Linearizability 不是面试题,是你线上事故复盘时需要的工具

九、本系列的"工程师视角"承诺

本系列不教你:

  • 抄写 Paxos / Raft 论文
  • 重新实现一个 etcd
  • 数学证明(只到关键不变量)

本系列教你:

  • 看懂 Kafka / etcd / ZK / TiDB 的设计文档"在抄哪一篇论文"
  • 选型时知道"为什么这个场景该选 Cassandra 不该选 ZK"
  • 出事故时知道"这个现象是 CAP 还是 FLP 还是 Linearizability 被破坏了"
  • 面试白板时讲清楚"为什么 Raft 比 Paxos 火,但 Paxos 不会被淘汰"

理论纵深 + 工程映射——这两个词后续会被反复实践。


十、踩坑提醒

  1. 以为"分布式 = 加机器"——真正难的是部分失败 + 网络不可靠 + 时钟漂移 + 状态分散
  2. 业务规模没到就上分布式——10 倍复杂度换 0 倍好处,典型的过度工程
  3. 以为"无状态服务"就没分布式问题——状态被你推给了下游,问题在下游照样存在
  4. 以为加节点就更可靠——节点越多故障点越多,冗余靠副本不是靠机器多
  5. 用 wall clock 给跨机事件排序——两台机器时钟差几十毫秒,因果可能颠倒
  6. 以为 TCP 可靠就网络可靠——TCP 给你字节流可靠,但连接断了、超时了、重连了,业务层不可靠
  7. 以为部署到 K8s 就分布式做对了——K8s 解决调度和容器编排,没解决一致性、共识、事务
  8. 以为分布式锁(Redis)能保证互斥——主从异步复制会丢锁,Redlock 也有争议(详见 26 篇)
  9. 以为 Kafka 是强一致——它只是"分区内有序 + ISR 同步复制",不是 Linearizability(详见 03 篇)
  10. 以为读了 DDIA 就懂分布式——DDIA 是地图,你还需要"走过坑"才知道地图哪里是悬崖

下一篇:02-八大谬误与故障模型.md,把 1994 年 Peter Deutsch 提出的"分布式八大谬误"配上 2010-2025 的真实事故复盘——这八条不是说教,是 30 年来全行业的血泪集合。然后引出更深一层:故障模型(Crash-stop / Crash-recovery / Omission / Byzantine)和时间模型(Synchronous / Asynchronous / Partial Synchronous)——这两组词汇是 09 篇 FLP 不可能定理的前置弹药。

最后更新: