← 返回博客列表

一、引言:什么是分布式系统

分布式系统(Distributed System)是由多台独立的计算机通过网络连接而成的一个整体系统,对用户而言它表现得就像一台计算机一样。这些计算机在物理上是分散的,在逻辑上是协同的,它们通过消息传递进行通信和协调,共同完成任务。

核心定义(软考标准表述)
分布式系统是若干独立计算机的集合,这些计算机对于用户来说就像是单个相关系统。其核心特征包括:
1. 资源独立性:每台计算机拥有自己的处理器、内存和资源
2. 通信网络化:节点之间通过消息传递(而非共享内存)通信
3. 并发执行:多个进程并发运行,需要协调与同步
4. 故障独立性:一个节点的故障不会导致整个系统崩溃
5. 透明性:对用户屏蔽系统的分布细节

1.1 分布式系统的核心目标

设计一个分布式系统,本质上是在追求以下几个核心目标,这些目标之间存在天然的张力,架构师需要根据业务场景进行权衡:

用户 透明的统一访问 网络(消息传递 / RPC / 消息队列) 节点 A CPU/内存/磁盘 副本数据 1 节点 B CPU/内存/磁盘 副本数据 2 节点 C CPU/内存/磁盘 副本数据 3 节点 D CPU/内存/磁盘 副本数据 4 节点间通过消息/协议协同(如 Paxos/Raft/Gossip)
图 1:分布式系统总体架构示意——多节点协同、对用户透明

1.2 分布式系统的核心挑战

分布式系统在带来扩展性和容错性的同时,也引入了一系列单机系统中不存在的难题,这正是后续章节要逐一解决的问题:

八异与九谬(Fallacies of Distributed Computing)
分布式系统初学者常犯的"九大谬误":1. 网络可靠;2. 延迟为零;3. 带宽无限;4. 网络安全;5. 拓扑不变;6. 管理员只有一个;7. 传输成本为零;8. 网络同构。
软考常考:理解这些谬误是设计健壮分布式系统的前提,"不要假设网络是可靠的"是分布式设计的金科玉律。

二、CAP 定理

CAP 定理是分布式系统最基础的理论之一,由 Eric Brewer 于 2000 年提出,2002 年由 Gilbert 和 Lynch 证明。它揭示了分布式系统在一致性、可用性、分区容错性三者之间的根本取舍。

CAP 定理标准表述
在一个分布式系统中,以下三个特性最多只能同时满足两个,不可能三者兼顾:
· C(Consistency,一致性):所有节点在同一时刻看到相同的数据
· A(Availability,可用性):系统持续可用,每个请求都能在有限时间内获得非错误响应
· P(Partition Tolerance,分区容错性):当网络发生分区(节点间消息丢失)时,系统仍能运行

2.1 三个特性的精确定义

2.2 为什么三者不能同时满足

直觉上理解:当网络发生分区时,节点 A 和节点 B 无法通信。此时如果用户向 A 写入数据 x=1,再向 B 读取 x:

C 一致性 A 可用性 P 分区容错 CP 强一致 AP 高可用 CA 无分区(单机) ZooKeeper etcd / HBase Eureka Cassandra DynamoDB
图 2:CAP 三角形——分布式系统只能取 CP 或 AP,CA 仅存在于单机

2.3 CP / AP / CA 系统举例

选择 含义 典型系统 适用场景
CP 保证一致性 + 分区容错,分区时牺牲可用性 ZooKeeper、etcd、HBase、MongoDB(默认) 金融账户、配置中心、分布式锁、元数据存储
AP 保证可用性 + 分区容错,分区时牺牲强一致 Eureka、Cassandra、DynamoDB、Riak 社交 feed、购物车、缓存、注册中心
CA 保证一致性 + 可用性,不允许分区(即单机) 单机 MySQL、单机 Redis 单机数据库(不存在网络分区问题)
案例:网络分区下 CP(ZooKeeper)vs AP(Eureka)
假设注册中心集群 5 个节点,因网络故障分裂为 {A,B} 与 {C,D,E} 两部分。
· ZooKeeper(CP):分区发生后,少数派 {A,B} 因不满足 Quorum(多数)会停止对外服务,保证已注册数据绝对一致;服务发现可能中断。
· Eureka(AP):两个分区都继续提供注册与发现服务,各自接受新注册,分区恢复后再异步合并。短时间可能出现脏数据,但服务发现"永远可用"。
结论:服务发现场景更看重可用性(找不到服务比返回错数据更严重),所以 Eureka、Nacos(AP 模式)常被选用;而分布式协调场景更看重一致性,故用 ZooKeeper、etcd。

三、BASE 理论

BASE 理论是对 CAP 中 AP 方案的工程化延伸,由 Dan Pritchett 提出,是大规模互联网系统的实践准则。它主张放弃强一致性,转而追求最终一致性,以换取高可用与高性能。

BASE 缩写含义
· B(Basically Available,基本可用):在故障情况下允许损失部分可用性(响应时间变长、降级服务、限流),但核心功能仍可用
· S(Soft state,软状态):允许系统存在中间状态,且该状态不影响系统整体可用性(如数据同步中的"副本不一致"状态)
· E(Eventually consistent,最终一致性):系统保证在没有新的更新操作时,所有副本最终会达到一致状态

3.1 最终一致性的变体

最终一致性并非铁板一块,根据保证强度的不同,可细分为以下几种:

3.2 BASE 与 CAP 的关系

BASE 是 CAP 中 AP 方案的具体落地。CAP 是理论,告诉我们"分区时必须在 C 和 A 之间二选一";BASE 是工程方法,告诉我们"选了 A 之后如何把 C 损失降到最低"——通过软状态和最终一致性,在可用性与一致性之间找到一个可接受的平衡点。

ACID(传统关系数据库) A · 原子性 Atomicity 事务要么全做要么全不做 C · 一致性 Consistency 事务前后数据约束不变 I · 隔离性 Isolation 并发事务相互隔离 D · 持久性 Durability 提交后永久保存 强一致 · 单机/小规模 BASE(分布式互联网) BA · 基本可用 允许降级、限流、响应变慢 S · 软状态 允许中间不一致状态 E · 最终一致性 副本最终达到一致 高可用 · 高性能 牺牲强一致换可用性 弱一致 · 大规模分布式
图 3:ACID 与 BASE 理论对比

3.3 ACID 与 BASE 对比

维度 ACID BASE
一致性强一致性最终一致性
可用性较低(锁定资源)高
性能较低高
扩展性差(难水平扩展)好(可水平扩展)
适用场景金融核心交易、库存社交、电商、互联网应用
典型系统MySQL、OracleCassandra、DynamoDB
对应 CAP偏向 CP偏向 AP
软考记忆口诀
ACID = "原一隔持"(原子、一致、隔离、持久)
BASE = "基软最"(基本可用、软状态、最终一致)
关系:BASE 是 AP 的工程实践,ACID 是传统单机数据库事务标准。

四、分布式一致性算法

一致性算法解决的核心问题是:让多个节点对某个值(提案/日志)达成一致。这是构建分布式协调服务、分布式锁、状态机复制(State Machine Replication)的基石。工业界最知名的两个算法是 Paxos 和 Raft。

4.1 Paxos 算法

Paxos 由图灵奖得主 Leslie Lamport 于 1990 年提出,是分布式一致性算法的"鼻祖",被认为是正确性最严格的算法,但以难以理解著称。Google Chubby、ZooKeeper 的 ZAB 协议都源自 Paxos 思想。

4.1.1 三种角色

4.1.2 Basic Paxos 流程

Basic Paxos 通过两个阶段达成一致:

  1. 阶段一:Prepare(准备)
    • Proposer 选定一个提案编号 N,向所有 Acceptor 发送 Prepare(N)。
    • Acceptor 收到 Prepare(N) 后:若 N 大于已承诺的所有编号,则承诺不再接受编号小于 N 的提案,并返回已接受的最高编号提案(若有);否则拒绝。
  2. 阶段二:Accept(接受)
    • 若 Proposer 收到多数派(Quorum,即过半) Acceptor 的 Promise,则发送 Accept(N, V),其中 V 是 Promise 中返回的最高编号提案值(若没有,则用自己的值)。
    • Acceptor 收到 Accept(N,V) 后,若未违背承诺,则接受该提案,并通知所有 Learner。
Proposer Acceptor1 Acceptor2 Acceptor3 阶段一 Prepare(N) Promise(N) [多数派] 阶段二 Accept(N, V) Accepted(N, V) Learner 已选定 V 核心:多数派 Quorum 决定 → 任意两个多数派必有交集 → 一致性
图 4:Basic Paxos 消息交互流程(Prepare → Promise → Accept → Accepted)
Paxos 的难点与局限
· 难理解:Lamport 用故事叙述,证明复杂,被戏称"两个学家也搞不懂"
· 难实现:Basic Paxos 只对单值达成一致,多值需 Multi-Paxos,工程实现细节多
· 活锁:多个 Proposer 互相抢占编号可能导致永远无法达成一致(需引入 Leader 优化)
· 工程上多以 Multi-Paxos + Leader 优化形式落地(如 Google Chubby、Spanner)

4.2 Raft 算法

Raft 由 Stanford 的 Diego Ongaro 在 2014 年提出,目标是"可理解的一致性算法"。它在正确性与 Paxos 等价的前提下,通过分解问题、强化 Leader 角色、约束状态空间,大幅降低了理解和实现难度。etcd、Consul、TiKV 等知名系统都采用 Raft。

4.2.1 三种角色与状态转换

Follower 被动接收 Candidate 发起选举 Leader 处理请求 选举超时 发现更高任期/收到Leader 获多数选票 发现更高任期 故障/网络分区(Leader 降级为 Follower) 任期(Term)单调递增,是 Raft 的逻辑时钟,任期高的胜出
图 5:Raft 三种角色状态转换图

4.2.2 日志复制流程

Raft 的核心机制是日志复制:客户端请求 → Leader 写入本地日志 → 复制到所有 Follower → 多数派确认后提交 → 响应客户端。

Client Leader 写入本地日志 Follower1 追加日志 Follower2 追加日志 Follower3 故障未响应 ①SET x=5 ②AppendEntries ③ACK(2/3多数派) ④提交 commit (idx=5) 应用到状态机 ⑤OK 响应 注:只需多数派(如 3 中 2)确认即可提交,故障节点后续补齐 Raft 强约束:日志连续、Term+Index 唯一标识、Leader 日志最完整 → 安全性
图 6:Raft 日志复制与提交流程

4.2.3 Raft 的三个子问题

4.3 Paxos 与 Raft 对比

维度 Paxos Raft
提出时间1990(Lamport)2014(Ongaro)
核心角色Proposer/Acceptor/LearnerLeader/Follower/Candidate
角色对称性多 Proposer 平等强 Leader 模型
可理解性难(论文晦涩)易(设计目标即"可理解")
工程实现复杂(Multi-Paxos 无标准)清晰(有完整规范)
典型系统Chubby、Spanner、ZABetcd、Consul、TiKV
消息复杂度两阶段两阶段(正常态)
选主不强制 Leader强制 Leader,简化流程
案例:etcd 使用 Raft
etcd 是 Kubernetes 的核心组件,用于存储集群元数据和配置。它采用 Raft 协议保证一致性:
· 通常部署 3 或 5 个节点(容忍 1 或 2 个节点故障)
· 所有写请求都由 Leader 处理,非 Leader 节点收到写请求会转发给 Leader
· 写操作需要多数派确认才提交,故 3 节点集群写入性能约等于单节点,但能容忍 1 节点宕机
· 读操作可由任意节点处理(默认 Linearizable Read 经 Leader 确认)
这就是为什么 etcd 是 CP 系统——牺牲部分可用性换取强一致性。

五、分布式锁

在单机系统中,可以用语言提供的互斥量(Mutex)保护临界区。但在分布式系统中,多个进程运行在不同机器上,无法使用单机锁,于是需要分布式锁——一种跨进程、跨机器的互斥机制。

分布式锁的核心要求
1. 互斥性:任意时刻只有一个客户端持有锁
2. 避免死锁:持有锁的客户端崩溃后,锁要能自动释放(设置过期时间 / 会话)
3. 可重入性(可选):同一客户端可多次获取同一把锁
4. 公平性(可选):按请求顺序获取锁
5. 高可用:锁服务自身要可靠

5.1 基于 Redis 的分布式锁

最经典的 Redis 分布式锁基于 SETNX(Set if Not eXists)。现代写法是:SET key value NX PX 30000,即"不存在则设置,并带 30 秒过期"。

Redlock 算法(Antirez 提出):为解决 Redis 主从异步复制导致锁丢失问题,在多个(通常 5 个)独立 Redis 实例上同时加锁,获得多数派(3 个)成功且耗时小于锁有效期,才认为加锁成功。

Client SETNX Redis Master key=lock, val=UUID Redis Slave 异步复制 ①加锁 NX PX ②异步同步 Lua 脚本判断 value 匹配? DEL ③释放锁 ⚠ 风险点 Master 宕机时锁未同步 → Redlock 多实例方案 AP 实现:性能高但非绝对可靠;Redlock 在一致性上仍有争议(Martin Kleppmann 质疑)
图 7:基于 Redis 的分布式锁(SETNX + Lua 释放 + Redlock)

5.2 基于 ZooKeeper 的分布式锁

ZooKeeper 的锁基于临时顺序节点(Ephemeral Sequential Node)实现,天然解决锁释放和公平性问题:

  1. 客户端在锁节点下创建临时顺序节点 /lock/node-00000001。
  2. 获取锁节点下所有子节点,判断自己是否是序号最小的;若是,则获得锁。
  3. 若不是,则监听前一个节点(比自己序号小且最大的)的删除事件(避免羊群效应)。
  4. 当前一个节点被删除时,自己被唤醒,再次检查是否最小,若最小则获得锁。
  5. 客户端会话断开时,临时节点自动删除,锁自动释放。
/lock node-00000001 ★ 持有锁(最小) node-00000002 watch ①号 node-00000003 watch ②号 Client A Client B Client C ZooKeeper 锁工作流程 ①创建临时顺序节点 ②查询判断是否最小 ③非最小则 watch 前一节点 ④前一节点删除被唤醒 ⑤会话断开自动删除(避免死锁) → 公平锁 CP 实现:强一致 + 公平 + 自动释放,但性能低于 Redis
图 8:基于 ZooKeeper 临时顺序节点的分布式锁

5.3 基于数据库的分布式锁

Client lock 表 (唯一索引) id | method | holder | expire 业务表 (version) UPDATE ... WHERE version=? INSERT 唯一索引 UPDATE 乐观锁 特点 实现简单、依赖少 性能差、瓶颈在DB 适合并发量低的场景
图 9:基于数据库的分布式锁(唯一索引 / 乐观锁 / 悲观锁)

5.4 三种实现方式对比

维度 Redis ZooKeeper 数据库
性能高(内存)中低
可靠性中(Redlock 提升)高(CP + 会话)中(依赖 DB HA)
公平性非公平公平(顺序节点)非公平/可加版本
自动释放过期时间会话失效自动删需事务/过期清理
实现复杂度中高低
适用场景高并发抢购、限流配置/协调、强一致低并发、轻量
案例:防止重复支付
用户点击"支付"按钮可能因网络卡顿被连点多次,导致重复扣款。解决方案:在支付接口加分布式锁——以 order_id 为 key,Redis 锁(适合高并发)或 ZK 锁(强一致)保证同一订单同时只有一个支付请求进入核心逻辑。结合数据库的幂等性设计(订单状态机:未支付→支付中→已支付),即使锁失效,状态校验也能兜底。

六、分布式事务

当一个业务操作涉及多个数据库或多个服务时,单机的本地事务无法保证跨节点的原子性,于是需要分布式事务。它是微服务架构下最难解决的问题之一,本质上是在一致性、可用性、性能之间权衡。

6.1 两阶段提交(2PC)

2PC(Two-Phase Commit)是经典的强一致性分布式事务方案,引入一个协调者(Coordinator)统一调度多个参与者(Participant)。

协调者 Coordinator 参与者 A 参与者 B 参与者 C 阶段一 Prepare? Prepare? Yes(锁定资源) Yes(锁定资源) 阶段二 Commit Commit Ack Ack 问题:同步阻塞 + 协调者单点 + 超时不确定性 + 数据不一致风险
图 10:2PC 两阶段提交流程
2PC 的致命缺陷
· 同步阻塞:参与者锁定资源期间一直阻塞,整个事务期间其他事务无法访问
· 协调者单点故障:协调者宕机后参与者一直阻塞锁定资源
· 超时不确定性:参与者收到 Commit 前宕机,恢复后无法确定事务状态
· 数据不一致:Commit 阶段部分参与者收不到消息,导致部分提交部分未提交

6.2 三阶段提交(3PC)

3PC 在 2PC 基础上增加一个 CanCommit 阶段,并把 Prepare 拆为 PreCommit,最终 DoCommit。引入超时机制:参与者超时未收到指令会自动提交(基于"大概率会提交"的假设),缓解阻塞。

协调者 参与者 参与者 ① CanCommit(询问) ② PreCommit(预提交) 执行事务不提交 ③ DoCommit(正式提交) 3PC vs 2PC ✓ 引入超时:参与者超时未收到 DoCommit 会自动提交(降低阻塞) ✗ 仍可能数据不一致 ✗ 多一轮交互增加延迟 ✗ 网络分区下仍有问题
图 11:3PC 三阶段提交流程(CanCommit → PreCommit → DoCommit)

6.3 TCC(Try-Confirm-Cancel)

TCC 是一种业务层面的两阶段提交,要求每个服务为每个操作提供三个接口:Try(资源预留)、Confirm(确认执行)、Cancel(取消释放)。相比 2PC 锁定资源,TCC 由业务自行管理资源,性能更高。

TCC 协调者 Try(冻结) Confirm(扣减) Cancel(解冻) 账户冻结额度 真正扣款 解冻额度 资源预留 全部Try成功→确认 任一Try失败→补偿 特点 性能高 侵入业务
图 12:TCC 模式(Try 资源预留 → Confirm 确认 / Cancel 补偿)
TCC 优点
  • 业务层两阶段,不锁数据库,性能高
  • Confirm/Cancel 幂等,可重试
  • 最终一致性,可用性好
TCC 缺点
  • 业务侵入大,每个操作需实现三个接口
  • 需保证 Confirm/Cancel 幂等性
  • 需要处理空回滚、悬挂问题
  • 开发成本高

6.4 Saga 模式

Saga 将一个长事务拆分为多个本地事务 T1、T2...Tn,每个 Ti 都有对应的补偿事务 Ci。若某步失败,则反向执行已完成步骤的补偿,最终达到一致。Saga 分两种协调方式:

T1 下单 T2 扣库存 T3 支付✗失败 触发补偿 C2 回库存 C1 取消单 Saga 特点 ✓ 最终一致,无长时间阻塞 ✓ 适合长事务、跨服务 ✗ 补偿逻辑复杂 ✗ 中间状态可见(脏读) ✗ 不保证隔离性
图 13:Saga 模式与补偿事务(T3 失败 → 反向执行 C2、C1)

6.5 本地消息表

本地消息表是一种最终一致性方案:业务数据和消息数据放在同一个数据库,在本地事务中同时写入业务表和消息表,保证业务与消息原子性。然后由后台任务轮询消息表,将消息投递到 MQ,下游消费后回调确认。它是可靠消息最终一致性的典型实现。

6.6 各种分布式事务方案对比

方案 一致性 性能 复杂度 适用场景
2PC强一致低中传统数据库跨库
3PC强一致低高理论改进,少用
TCC最终一致高高高并发金融交易
Saga最终一致高中长流程业务编排
本地消息表最终一致高低异步解耦场景
事务消息(RocketMQ)最终一致高中电商下单+异步
案例:电商订单 + 支付 + 库存事务
下单流程涉及订单服务、库存服务、支付服务三个独立数据库。
· 强一致方案(2PC):性能差,互联网基本不用
· TCC:Try 阶段冻结库存 + 创建订单(待支付) + 冻结额度;Confirm 全部扣减;Cancel 全部回滚。适合秒杀
· Saga:T1 创建订单 → T2 扣库存 → T3 支付;若 T3 失败,反向执行 C2(回库存)→C1(取消订单)
· 事务消息(RocketMQ):订单服务发送"半消息"→执行本地事务→提交/回滚消息→库存服务消费扣减。最终一致,最常用
选型:互联网电商主流是 事务消息 + Saga,金融核心用 TCC。

七、负载均衡

负载均衡(Load Balancing)是将请求分发到多台后端服务器的技术,目标是提高吞吐、避免单点过载、提升可用性。按位置可分为服务端负载均衡和客户端负载均衡。

7.1 服务端 vs 客户端负载均衡

客户端 负载均衡器 Nginx/LVS/F5 按算法分发 Server1 Server2 Server3 Server4 健康检查 剔除故障节点 自动恢复 服务端 LB:客户端只感知 LB,LB 维护后端列表 + 健康检查 + 转发
图 14:服务端负载均衡架构

7.2 常见负载均衡算法

哈希空间 0 ~ 2^32 A Node A B Node B C Node C D Node D K1→B K2→C K3→D Key 顺时针找最近的 Node
图 15:一致性哈希环——Key 沿顺时针路由到最近的节点

7.3 算法对比

算法 原理 优点 缺点
轮询依次分发简单忽略性能差异
加权轮询按权重分发考虑性能差异权重需手动维护
最少连接选连接数最少自适应负载计算开销略大
IP HashIP 哈希固定会话保持节点变化导致大量迁移
一致性哈希环状哈希空间节点增减影响小需虚拟节点均衡

八、分布式 ID 生成

分库分表后,单表自增 ID 失效,需要全局唯一 ID 生成方案。一个合格的分布式 ID 应满足:全局唯一、趋势递增、高可用、高性能。

8.1 常见方案

Snowflake 64 位 ID 结构 1 符号 41 bit 时间戳(毫秒) 可用 69 年 10 bit WorkerID 1024 个节点 12 bit 序列号 每毫秒 4096 个 Snowflake 特点 ✓ 趋势递增:高位是时间戳,整体随时间增长 ✓ 本地生成:无网络开销,每毫秒可生成 4096 个 ✗ 时钟回拨问题:机器时钟回退可能产生重复 ID(需用百度 UidGenerator 等方案规避) ✓ 可扩展:5 位数据中心 + 5 位机器 ✓ 高性能:本地计算,无中心瓶颈 ✗ WorkerID 分配需协调(ZK/DB)
图 16:Snowflake 雪花算法 64 位 ID 位布局

8.2 方案对比

方案 唯一性 有序性 性能 依赖
UUID极高无序高无
Snowflake高趋势递增极高WorkerID 分配
号段模式高递增高数据库
Redis INCR高递增高Redis
ZooKeeper高递增低ZK
软考要点
· Snowflake 64 位分配:1 符号位 + 41 时间戳 + 10 机器 + 12 序列
· 趋势递增 ≠ 严格递增(同一毫秒内按序列递增,跨毫秒可能小于上一毫秒最后值)
· 工业界主流:美团 Leaf(号段+Snowflake 双 buffer)、百度 UidGenerator、滴滴 Tinyid

九、CDN 与缓存架构

缓存是分布式系统提升性能、降低数据库压力的核心手段。从浏览器到数据库,每一层都可以缓存,构成多级缓存架构。

9.1 CDN 概念与架构

CDN(Content Delivery Network,内容分发网络)通过在各地部署边缘节点,将静态内容缓存到离用户最近的节点,使用户就近获取数据,降低延迟、减轻源站压力。其核心是 DNS 智能调度:用户请求域名时,DNS 返回距离用户最近的 CDN 节点 IP。

用户 DNS 调度 ①请求 ②返回最近节点IP CDN 节点(北京) CDN 节点(上海) CDN 节点(广州) ③就近访问 源站 回源拉取 ④缓存未命中回源 CDN 流程:用户→DNS 调度→就近边缘节点→命中则返回,未命中回源并缓存
图 17:CDN 请求流程与 DNS 智能调度

9.2 多级缓存架构

大型互联网系统通常采用多级缓存,请求自上而下逐层命中,只有所有缓存未命中才访问数据库:

  1. 浏览器缓存:通过 Cache-Control、Expires、ETag 控制,减少请求次数。
  2. CDN 缓存:静态资源(图片、JS、CSS)缓存在边缘节点。
  3. Nginx 缓存:反向代理层缓存,proxy_cache 共享内存或磁盘。
  4. 本地缓存(应用内):如 Guava Cache、Caffeine,进程内缓存,极低延迟。
  5. 分布式缓存:如 Redis Cluster、Memcached,跨进程共享。
  6. 数据库:最后防线,自身也有 Buffer Pool 缓存。
请求 L1 浏览器缓存 Cache-Control/ETag L2 CDN 缓存 边缘节点 L3 Nginx 缓存 proxy_cache 未命中↓ L4 本地缓存 Caffeine/Guava L5 分布式缓存 Redis Cluster L6 数据库 MySQL + Buffer Pool 最终回源 各级缓存延迟对比(数量级) · 浏览器缓存:0 ms(本地读取) · CDN 缓存:10~50 ms(就近节点) · Nginx 缓存:1~5 ms(同机房) · 本地缓存:0.01~0.1 ms(进程内) · Redis 缓存:0.5~2 ms(网络往返) · 数据库:5~50 ms(磁盘 IO) 原则:能近不远、能缓不查;越靠上层性能越好
图 18:多级缓存架构与延迟对比

9.3 缓存三大经典问题

缓存并非银弹,使用不当会引发三类典型故障:穿透、击穿、雪崩。软考高频考点,必须能区分并给出解决方案。

9.3.1 缓存穿透(Cache Penetration)

指查询一个根本不存在的数据,缓存和数据库都没有,每次请求都打到数据库。常见于恶意攻击(用大量不存在的 ID 请求)。

解决方案:

缓存穿透:查询不存在的数据 恶意请求 id=-1 Redis 缓存 未命中 数据库 查不到 ⚠ 每次都打 DB 解决方案 ① 缓存空值:DB 查不到也写入 null(TTL 短,如 60s),下次直接返回 ② 布隆过滤器:请求先查 BF,不存在直接拒绝(空间高效,有误判率但无漏判) ③ 接口层做参数校验,过滤非法 ID(如负数、超范围) 口诀:穿透 = 查不存在 → 缓存空值 + 布隆过滤器
图 19:缓存穿透问题与解决方案

9.3.2 缓存击穿(Cache Breakdown)

指一个热点 Key 在缓存过期的瞬间,大量并发请求同时打到数据库,造成 DB 瞬时压力激增。区别于穿透(数据不存在),击穿的数据是存在的,只是缓存恰好失效。

解决方案:

缓存击穿:热点 Key 过期瞬间 Req1 Req2 Req3 大量并发 热点 Key 刚好过期! 数据库 瞬时被打爆 ⚠ 同一热点 解决方案 ① 互斥锁:未命中时加分布式锁,仅一个线程查 DB 回填,其余等待重试 ② 热点永不过期:不设 TTL,逻辑过期 + 后台异步刷新 口诀:击穿 = 单点热点过期 → 互斥锁 + 永不过期
图 20:缓存击穿问题与解决方案

9.3.3 缓存雪崩(Cache Avalanche)

指大量 Key 在同一时间集中过期,或者 Redis 整体宕机,导致大量请求同时打到数据库,引发 DB 雪崩式崩溃。区别于击穿(单个热点),雪崩是大面积失效。

解决方案:

缓存雪崩:大量 Key 同时过期 Key1 过期 Key2 过期 Key3 过期 ...大量 同一时刻失效 数据库 压力暴增 崩溃! 解决方案 ① TTL 加随机值:expire = base + random(),打散过期时间 ② Redis 高可用:主从+哨兵+Cluster,避免整体宕机 ③ 多级缓存 + 限流降级:本地缓存兜底,DB 限流保护 口诀: 雪崩 = 大面积失效 → 随机 TTL + HA
图 21:缓存雪崩问题与解决方案
三大问题对比记忆
· 穿透:数据不存在 → 缓存空值 + 布隆过滤器
· 击穿:单个热点过期 → 互斥锁 + 永不过期
· 雪崩:大量 Key同时过期 → 随机 TTL + 高可用
一句话:穿透是"没有",击穿是"一个崩",雪崩是"一片崩"。

十、一致性哈希

一致性哈希(Consistent Hashing)由 MIT 的 Karger 等人于 1997 年提出,主要解决分布式缓存节点增减时大量 key 重新映射的问题,是负载均衡和分片的核心算法。

10.1 为什么需要一致性哈希

传统哈希取模法:hash(key) % N,N 为节点数。当 N 变化(增减节点)时,几乎所有 key 都会重新映射,导致大面积缓存失效,瞬间击穿数据库。一致性哈希将哈希空间组织成环,节点增减只影响相邻区间的 key,迁移量最小。

传统哈希取模(N 变化全乱) hash(key) % 4 Node0 Node1 Node2 Node3 → 加节点变 N=5 几乎所有 key 重映射!缓存全失效 一致性哈希(N 变化局部) A B C D → 加节点 E 仅 E 相邻区间 key 迁移 其余 key 不变! 虚拟节点(Virtual Node)解决数据倾斜 · 真实节点少时,环上分布不均 → 某节点负载过高(数据倾斜) · 每个物理节点映射多个虚拟节点(如 150~200 个)到环上 · 虚拟节点越多,分布越均匀;key 定位到虚拟节点再映射到物理节点 典型:Memcached、Redis Cluster(16384 槽位)
图 22:一致性哈希与虚拟节点——节点增减只影响相邻区间

10.2 节点增减的影响分析

一致性哈希的关键性质:当增加或删除一个节点时,只有该节点顺时针相邻区间的 key 需要迁移,其他 key 保持不变。迁移量约为 1/N(N 为节点数),远优于取模法的全部迁移。

案例:Redis Cluster 分片
Redis Cluster 没有直接用一致性哈希环,而是用哈希槽(Hash Slot):固定 16384 个槽,每个 key 经 CRC16 哈希后对 16384 取模定位槽位,槽位均匀分配到各节点。
· 扩容:新节点加入时,从现有节点迁移部分槽位(slot 迁移)
· 缩容:先迁移该节点所有槽位到其他节点,再下线
· 优势:槽位离散、迁移可在线进行、客户端可用MOVED重定向
这本质是预分配的一致性哈希变体,避免了虚拟节点映射开销,是工业界优秀实践。

十一、分布式系统实战案例:电商秒杀系统

秒杀是分布式系统综合能力的"试金石":瞬时高并发、库存超卖、防刷、支付一致性等问题集中爆发。下面用前面所学理论设计一个秒杀系统。

11.1 整体架构

用户 CDN Nginx 限流+负载均衡 网关 鉴权+令牌桶 应用节点1 应用节点2 应用节点N Redis 集群 库存预扣/Lua原子 消息队列 异步下单 MySQL 最终落库 核心思想:层层拦截、异步削峰、缓存先行、DB 兜底
图 23:电商秒杀系统整体架构

11.2 关键技术点

限流:令牌桶与漏桶

异步削峰

秒杀请求经 Redis 预扣库存成功后,不直接写库,而是发送消息到 MQ,由下游消费者异步创建订单、扣减 DB 库存。这样将瞬时高并发转为平稳消费,保护数据库。

库存扣减防超卖

使用 Redis 的 Lua 脚本原子执行"判断库存 + 扣减":if stock > 0 then decr stock end。Lua 在 Redis 中单线程执行,天然原子,避免超卖。

秒杀请求完整流程 ①点击秒杀 ②Nginx限流 ③网关鉴权 ④Redis Lua 预扣库存 ⑤库存不足? 是→直接拒绝 ⑥发送MQ消息 否(扣减成功) ⑦异步消费者 ⑧DB 创建订单 扣减真实库存 ⑨支付 ⑩完成/超时取消 设计要点总结 ① 静态资源 CDN + 页面静态化,减少应用压力 ② 多层限流:Nginx(令牌桶) → 网关 → 应用(本地限流),层层削减 ③ 库存预热到 Redis,Lua 脚本原子扣减,杜绝超卖 ④ MQ 异步下单削峰,DB 平稳消费 ⑤ 防刷:验证码 + IP 限频 + 同用户去重 ⑥ 容灾:服务降级、熔断、限流保护 ⑦ 分布式事务:Redis 预扣 + MQ + DB 最终一致 ⑦ 容器化 + 弹性伸缩应对流量洪峰 口诀:缓存拦、限流削、异步化、原子扣
图 24:秒杀请求完整流程与设计要点

十二、软考考点总结

分布式系统是系统架构设计师考试的核心重难点,涉及理论、算法、工程实践多个层面。下面梳理高频考点与记忆要点。

12.1 核心理论与公式

必背理论
1. CAP 定理:C/A/P 三者只能取其二;分布式系统 P 必选,故在 CP 与 AP 间权衡
2. BASE 理论:基本可用 + 软状态 + 最终一致性(AP 的工程实践)
3. Quorum 机制:N 个副本,W 写确认,R 读确认,满足 W + R > N 即可保证强一致
4. FLP 不可能定理:异步网络中,即使只有 1 个节点故障,也无法存在确定性的一致性算法
5. 共识 vs 一致性:共识(Consensus)是多节点就值达成一致;一致性(Consistency)是数据状态一致

12.2 算法与协议对比

主题 核心要点 典型系统
PaxosProposer/Acceptor/Learner;两阶段;多数派Chubby、Spanner
RaftLeader/Follower;强 Leader;日志复制etcd、Consul、TiKV
ZABZK 原子广播;类似 Paxos;Leader 选举ZooKeeper
Gossip流行病协议;最终一致;去中心化Cassandra、Redis Cluster
2PC协调者+参与者;Prepare+Commit;阻塞XA 规范
3PCCanCommit+PreCommit+DoCommit;超时理论改进
TCCTry/Confirm/Cancel;业务两阶段蚂蚁 DTX、Hmily
Saga长事务拆分+补偿;编排/协同AWS Step Functions

12.3 常见题型与答题技巧

12.4 记忆口诀汇总

高效记忆口诀
· CAP:"分区必选 P,强一致选 CP,高可用选 AP"
· BASE:"基软最"——基本可用、软状态、最终一致
· Paxos 两阶段:"一准二接"——Prepare/Promise、Accept/Accepted
· Raft 三角色:"跟随-候选-领导",靠任期 Term晋升
· 2PC 缺陷:"阻、单、不"——同步阻塞、协调者单点、数据不一致
· 缓存三问:"穿不存在、击单热点、雪大面积"
· 分布式锁三方案:"Redis 快、ZK 稳、DB 简"
· Snowflake:"1-41-10-12"(符号-时间-机器-序列)
· 一致性哈希:"环上走,顺时针,虚拟节点防倾斜"
· 秒杀设计:"缓存拦、限流削、异步化、原子扣"

12.5 高频易错点

考试易错点提醒
1. CAP 的 C 是线性一致性,不是 ACID 的一致性,二者含义不同
2. BASE 的 C 是最终一致性,弱于 ACID 的强一致性
3. Paxos 多数派指"过半",3 节点需 2 票,5 节点需 3 票
4. Raft 任期单调递增,过期 Leader 收到更高 Term 会自动降级
5. 缓存击穿 vs 雪崩:击穿是"一个热点",雪崩是"大量 Key"
6. 令牌桶 vs 漏桶:令牌桶允许突发,漏桶平滑输出
7. 2PC 的同步阻塞发生在 Prepare 阶段(资源锁定到 Commit)
8. TCC 的 Confirm/Cancel 必须幂等,因为可能被重试
9. 一致性哈希迁移量 1/N,N 为节点数;虚拟节点解决倾斜
10. Redis Cluster 是 16384 槽,不是一致性哈希环

分布式系统的精髓在于权衡(Trade-off):没有完美方案,只有适合场景的方案。架构师的价值在于根据业务对一致性、可用性、性能、成本的优先级,选择并组合恰当的理论与工程实践。希望本文能帮助你在软考和实际工作中建立系统的分布式思维。

复习建议
1. 先理解理论(CAP/BASE/FLP),再学算法(Paxos/Raft),最后落地工程(锁/事务/缓存)
2. 多画图:CAP 三角、Paxos 时序、一致性哈希环、缓存问题对比图
3. 结合实际项目:把每个概念对应到自己系统的某个组件
4. 论文素材:准备 1~2 个分布式项目(如秒杀、订单系统),按"问题-方案-效果"组织