【系统架构师案例题】分布式系统设计与选型

文章目录

    • 一、先建立整体认识
    • [二、CAP 与 BASE](#二、CAP 与 BASE)
      • [1. CAP:分区发生时你到底保什么](#1. CAP:分区发生时你到底保什么)
      • [2. BASE:为什么很多互联网系统接受最终一致](#2. BASE:为什么很多互联网系统接受最终一致)
    • 三、分布式事务
      • [1. 为什么会有分布式事务](#1. 为什么会有分布式事务)
      • [2. 常见方案](#2. 常见方案)
        • [2.1 2PC:用协调者强行统一提交](#2.1 2PC:用协调者强行统一提交)
        • [2.2 3PC:在 2PC 上继续减阻塞](#2.2 3PC:在 2PC 上继续减阻塞)
        • [2.3 TCC:把提交和回滚前移到业务层](#2.3 TCC:把提交和回滚前移到业务层)
        • [2.4 Saga:把大事务拆成多段本地事务](#2.4 Saga:把大事务拆成多段本地事务)
      • [3. 分布式事务怎么选,怎么考](#3. 分布式事务怎么选,怎么考)
    • 四、分布式锁
      • [1. 为什么会有分布式锁](#1. 为什么会有分布式锁)
      • [2. 常见实现](#2. 常见实现)
      • [2.1 数据库锁:简单,但不适合高并发主链路](#2.1 数据库锁:简单,但不适合高并发主链路)
      • [2.2 Redis 锁:快,但要小心边界](#2.2 Redis 锁:快,但要小心边界)
      • [2.3 ZooKeeper / Etcd 锁:更稳,但更重](#2.3 ZooKeeper / Etcd 锁:更稳,但更重)
      • [3. 分布式锁怎么选,怎么考](#3. 分布式锁怎么选,怎么考)
    • [考试里不要只答"Redis 性能高,ZK 可靠性高"。更好的写法是:**业务最怕什么错误,就优先选能避免这种错误的方案**。例如秒杀防重更看重性能,主节点选举更看重正确性。](#考试里不要只答“Redis 性能高,ZK 可靠性高”。更好的写法是:业务最怕什么错误,就优先选能避免这种错误的方案。例如秒杀防重更看重性能,主节点选举更看重正确性。)
    • 五、负载均衡与一致性哈希
      • [1. 负载均衡解决什么问题](#1. 负载均衡解决什么问题)
      • [2. 为什么一致性哈希很重要](#2. 为什么一致性哈希很重要)
    • 六、服务治理
      • [1. 服务注册与发现:解决"服务活着,但你找不到它"](#1. 服务注册与发现:解决“服务活着,但你找不到它”)
      • [2. 限流、熔断、降级:它们都在保护系统,但保护方式不同](#2. 限流、熔断、降级:它们都在保护系统,但保护方式不同)
      • [3. 三者怎么区分,怎么考](#3. 三者怎么区分,怎么考)
    • 七、微服务拆分原则
    • [**行业里的经验判断:** 当团队已经因为单体代码过大、发布互相影响、热点能力无法独立扩容而明显受限时,微服务拆分才真正有收益。如果这些问题还不明显,过早拆分往往是在提前引入复杂度。](#行业里的经验判断: 当团队已经因为单体代码过大、发布互相影响、热点能力无法独立扩容而明显受限时,微服务拆分才真正有收益。如果这些问题还不明显,过早拆分往往是在提前引入复杂度。)
    • 八、答题模板
      • [1. 概念解释题](#1. 概念解释题)
      • [2. 对比题](#2. 对比题)
      • [3. 选型题](#3. 选型题)
      • [4. 常见卷面句式](#4. 常见卷面句式)

一、先建立整体认识

分布式系统不是把一个程序部署到多台机器上这么简单,它真正带来的变化是:原来单机里那些"默认成立"的前提开始失效了。

  • 单机里函数调用很快,分布式里远程调用会超时、重试、部分失败
  • 单机里一个数据库事务就能兜底,分布式里一个业务流程会跨多个服务和多个库
  • 单机里内存就是当前事实,分布式里不同节点可能在同一时刻看到不同数据
  • 单机里加锁只锁一个进程,分布式里多个实例会同时抢同一份资源

所以,分布式设计本质上是在回答四类问题:

  • 数据不一致时,系统优先保什么
  • 跨服务操作时,系统如何收敛到正确状态
  • 多个实例同时工作时,系统如何避免互相踩踏
  • 服务规模变大后,系统如何维持可观测、可治理、可扩展

二、CAP 与 BASE

1. CAP:分区发生时你到底保什么

怎么理解: CAP 不是让你在正常情况下三选二,而是在网络分区真的发生时,系统必须在一致性和可用性之间做取舍。

特性 含义 软件行业里的直观理解
C(Consistency) 所有节点在同一时刻看到的数据一致 用户不管打到哪个节点,看到的结果都一样
A(Availability) 每个请求都能在合理时间内收到非错误响应 系统尽量别报错,先给响应
P(Partition tolerance) 网络分区发生时系统仍能继续运行 机房抖动、节点失联时,系统不能直接整体瘫痪

为什么必须先保 P: 真正上线的系统一定会遇到网络抖动、机房隔离、节点超时,所以分布式系统没法假设"网络永远可靠"。也就是说,P 不是偏好,而是前提。真正能选的只剩下 CPAP

  • CP:更看重数据绝对一致,宁可暂时拒绝部分请求
  • AP:更看重系统持续可响应,允许短时间数据不一致

放到真实场景里看:

  • 配置中心、注册中心、选主系统通常更偏 CP。如果主节点认错、配置版本乱了,后果往往比短时间不可用更严重。
  • 商品浏览、评论计数、推荐结果、DNS 解析通常更偏 AP。这类系统首先要顶住访问,允许短暂延迟同步。
  • 电商下单链路往往是"局部 CP,整体 AP"。例如扣减库存的关键步骤要更谨慎,但订单列表、优惠券展示、消息通知可以最终一致。

怎么考: 题干通常不会只问 CAP 定义,而是会给你一个场景,让你判断为什么这里宁可等待一致,也不愿意先返回;或者为什么这里允许短暂不一致。答题时不要只写"CAP 最多三选二",要写清楚这个业务最怕什么风险

2. BASE:为什么很多互联网系统接受最终一致

怎么理解: BASE 可以看成是 AP 思路在工程上的落地版本。它承认分布式环境里很难像单库事务那样处处强一致,于是把目标改成"核心可用、过程允许中间状态、最终能收敛正确"。

缩写 含义 软件行业里的典型表现
BA(Basically Available) 基本可用 高峰期允许排队、降级、稍慢,但核心交易还能走
S(Soft State) 软状态 缓存和副本在某一刻可以不完全同步
E(Eventually Consistent) 最终一致 通过消息、重试、补偿,让数据稍后收敛一致

和 ACID 的差别:

维度 ACID BASE
目标 立即正确 最终正确
代价 吞吐和扩展性更受约束 业务要承担补偿和幂等复杂度
常见位置 单库事务、核心账务 微服务、消息驱动、异步解耦

行业里的常见例子: 用户支付成功后,订单状态、积分、优惠券、营销消息未必同一瞬间全更新,但系统会通过 MQ、重试和补偿让这些数据逐步收敛。这就是典型的 BASE 思路。


三、分布式事务

1. 为什么会有分布式事务

单机事务之所以好用,是因为一个数据库能统一决定提交还是回滚。可一旦业务拆成多个服务,这个前提就没了。

最典型的行业场景是下单:

  • 订单服务要创建订单
  • 库存服务要扣库存
  • 支付服务要冻结或扣减金额
  • 营销服务要核销优惠券

如果其中前两步成功,第三步失败,系统就会进入"局部成功、整体失败"的尴尬状态。分布式事务要解决的,就是这种跨服务流程如何最终收敛正确的问题。

2. 常见方案

2.1 2PC:用协调者强行统一提交

怎么理解: 2PC 的思路很像"总控拍板"。协调者先问每个参与者能不能提交,如果所有人都说可以,再统一下达提交命令。

阶段 发生什么
准备阶段 协调者询问,参与者先执行但不真正提交
提交阶段 全员同意则一起提交,否则一起回滚

适合什么场景: 更偏传统数据库和强一致要求高的场景,例如一些历史中间件、XA 事务体系。

工程上的问题: 它最致命的不是"理论上复杂",而是现实里会阻塞。参与者在等最终命令时会占着资源不放,协调者一旦卡住,整个链路就可能挂在那里。所以互联网高并发系统一般不喜欢把关键交易建在 2PC 上。

2.2 3PC:在 2PC 上继续减阻塞

怎么理解: 3PC 在 2PC 基础上多加了一层"预提交"和超时机制,试图减少长时间阻塞。

它的改进方向是对的,但在真实工程里并没有成为主流。原因很简单:复杂度显著上升,但仍不能从根本上消灭网络异常导致的不一致风险。考试里知道它是"2PC 的改进版"就够了,答题时不用把它写成互联网主流实践。

2.3 TCC:把提交和回滚前移到业务层

怎么理解: TCC 不再依赖数据库帮你兜底,而是把事务拆成三步:

阶段 行业里的直观理解
Try 先预留资源,例如冻结库存、冻结余额
Confirm 业务确认成功,再真正扣减
Cancel 业务失败,释放之前冻结的资源

为什么很多支付场景会用它: 支付、转账、账户冻结这类业务往往不能简单依赖最终补偿,因为钱这个东西对用户感知太强,也经常要求比较明确的资源预占。TCC 的优势就在于业务语义清晰,性能通常也比全局锁协调更好。

真正的代价: 每个业务都要自己写 Try / Confirm / Cancel 三套逻辑,而且必须处理幂等、空回滚、防悬挂。这不是一个"会背概念就能落地"的方案,业务能力不强的团队很容易写出一堆边界 Bug。

2.4 Saga:把大事务拆成多段本地事务

怎么理解: Saga 的思路不是所有步骤一起锁住等结果,而是让每一步先按本地事务正常提交;如果后面某一步失败,再把前面已经做过的步骤按顺序补偿回来。

比如旅游平台一次下单可能涉及机票、酒店、优惠券、积分,每一步都可以先独立完成;如果酒店预订失败,就撤销机票预占、退回优惠券、回补积分。这就是 Saga 最常见的工程心智模型。

两种组织方式:

  • 编排式:各服务靠事件自己接力,耦合更松,但全局链路更难看清
  • 协调式:由一个 Saga 协调器统一调度,可观测性更好,但中心控制更强

为什么它在行业里很常见: 很多互联网业务不是"绝对不能错一步",而是"允许稍后纠正,但整体不能卡死"。Saga 正适合这种长流程、跨服务、对吞吐更敏感的场景。

3. 分布式事务怎么选,怎么考

方案 一致性 性能 实现难度 更像什么场景
2PC 强一致 较差 传统强事务体系
3PC 强一致 一般 理论延伸、考试常客
TCC 最终一致偏强控制 较好 很高 支付、转账、账户类业务
Saga 最终一致 较好 订单、履约、长流程业务

答题时最重要的不是把四个方案都背全,而是抓住一句话:

  • 要强一致,通常代价更高
  • 要吞吐和可扩展,通常要接受最终一致
  • 越靠业务层解决问题,越灵活,也越依赖团队实现能力

四、分布式锁

1. 为什么会有分布式锁

只要系统做了多实例部署,就会出现"多个实例同时盯上同一个资源"的问题。

典型场景有:

  • 定时任务集群里,只希望一个实例真正执行结算
  • 订单超时取消时,不希望两个实例同时修改同一订单状态
  • 库存重算、优惠券发放、批处理补偿时,不希望重复执行

这时本地 synchronized 或单机锁已经没用了,因为不同实例根本不在同一个进程里。分布式锁就是为了在多实例之间建立互斥。

2. 常见实现

2.1 数据库锁:简单,但不适合高并发主链路

怎么理解: 最朴素的做法,就是在数据库里插一条锁记录,谁先插入成功谁拿到锁;或者直接用行锁。

优点: 好理解、实现门槛低、审计方便。很多内部系统、低频后台任务用它就够了。

缺点: 数据库容易成为瓶颈,没有天然过期机制,异常退出还可能锁泄漏。所以它更适合低并发、低频场景,不适合秒杀、抢券这类热点路径。

2.2 Redis 锁:快,但要小心边界

怎么理解: Redis 锁之所以流行,不是因为它理论最完美,而是因为它 。最常见的写法是 SET key value NX EX timeout

  • NX 保证只有 key 不存在时才能成功
  • EX 给锁一个过期时间,避免死锁
  • value 用随机值标识持有者,释放时先校验再删除

为什么互联网业务经常选它: 像下单防重、任务互斥、短时间资源抢占,很多时候首先看的是性能和延迟,Redis 在这方面非常有优势。

工程上真正要注意的点:

  • 释放锁必须校验持有者,不能直接 DEL
  • 业务执行时间可能超过过期时间,需要续租
  • Redis 主从切换时可能发生锁丢失

所以 Redis 锁适合"高性能优先、允许工程上做补偿"的场景,但不要把它神化成绝对可靠锁。

2.3 ZooKeeper / Etcd 锁:更稳,但更重

怎么理解: ZooKeeper 和 Etcd 这类系统本身就更强调强一致和会话语义,因此更适合做"谁拥有锁"这种需要明确判断的事情。

  • ZooKeeper 常用临时顺序节点实现锁。连接断开,节点会自动删除,所以天然更容易防死锁。
  • Etcd 常用租约和事务实现锁,背后依赖 Raft 一致性协议,语义也比较清晰。

它们为什么常出现在基础设施里: 配置中心、服务注册、主节点选举、调度系统这类场景,更怕"判错主"而不是"多等几十毫秒"。这时比起极致性能,更重要的是语义可靠。

3. 分布式锁怎么选,怎么考

方案 性能 可靠性 适合什么场景
数据库 一般 低频后台任务、内部系统
Redis 一般 高并发互斥、短事务防重
ZooKeeper 选主、协调、基础设施控制面
Etcd 云原生控制、租约式协调

考试里不要只答"Redis 性能高,ZK 可靠性高"。更好的写法是:业务最怕什么错误,就优先选能避免这种错误的方案。例如秒杀防重更看重性能,主节点选举更看重正确性。

五、负载均衡与一致性哈希

1. 负载均衡解决什么问题

当系统从单实例走向集群后,请求必须知道"该落到哪一台机器"。负载均衡就是在解决这个问题,它的目标不只是把流量摊平,更重要的是让系统在扩容、故障、性能差异存在时仍能稳定工作。

常见算法可以这样理解:

算法 更适合怎么记
轮询 一台一台轮着来,最简单
加权轮询 机器能力不一样时,强机多分一点
随机 简单粗暴,请求足够多时也能趋于均匀
最少连接 谁现在更空,就给谁,适合长连接
IP 哈希 同一客户端尽量打到同一台,便于会话粘性

在真实行业里,负载均衡并不是只存在于 Nginx。它也存在于注册中心客户端选实例、服务网格选后端、消息分区选消费者等地方。

2. 为什么一致性哈希很重要

普通哈希最大的问题是:节点一变,映射几乎全乱。这在缓存和分片场景里代价很大。

比如电商缓存有 5 台 Redis,商品详情页的缓存 key 都按哈希分布。现在新加 1 台机器,如果用普通取模,大量 key 的归属都会变化,结果就是缓存雪崩式失效,数据库瞬间被打穿。

一致性哈希就是为了解决这个问题:

  • 把节点放到一个哈希环上
  • 把数据 key 也映射到环上
  • key 顺时针找到的第一个节点,就是它的归属节点

这样做的好处是,节点增减时,只影响局部区间的数据迁移,而不是全盘重算 。为了避免数据倾斜,工程上通常会引入虚拟节点

怎么考: 题干如果出现"缓存扩容后数据大量重映射""希望节点增减时迁移最少""分布式缓存/分片存储",优先想到一致性哈希。

六、服务治理

1. 服务注册与发现:解决"服务活着,但你找不到它"

微服务一多,服务实例地址就不可能手工维护了。今天 order-service 有 3 个实例,明天扩到 10 个,后天又缩回 4 个,如果调用方还写死 IP,系统根本没法运转。

所以服务治理的第一步,就是把"谁在线、地址在哪、状态如何"交给注册中心统一管理。

组件 作用
服务注册中心 保存实例地址和元信息,如 Nacos、Consul、Eureka、ZooKeeper
服务注册 服务启动后把自己报到注册中心
服务发现 调用方查询可用实例列表
健康检查 剔除不可用实例,避免流量继续打过去

行业里的直观理解: 注册中心就像动态通讯录。服务不是写死联系地址,而是先去通讯录里查"当前谁可用"。

2. 限流、熔断、降级:它们都在保护系统,但保护方式不同

很多人把这三个词背熟了,但写题时还是容易混。真正好记的方法是先看它们分别在防什么风险。

限流: 防的是"流量太大,把自己压垮"。

  • 秒杀、热点活动、刷接口时最常见
  • 典型算法包括固定窗口、滑动窗口、漏桶、令牌桶
  • 行业里最常用的理解是:系统能力有限,超出的流量必须排队、拒绝或削峰

熔断: 防的是"下游已经出问题了,还继续把自己拖死"。

  • 例如商品服务调用价格服务,价格服务超时率暴涨
  • 这时如果商品服务还不停重试,就会把线程池、连接池都耗光
  • 熔断器的核心动作是快速失败,先切断这条有毒链路

常见状态有三种:

  • 关闭:正常调用,持续统计失败率
  • 打开:达到阈值后直接失败,不再打下游
  • 半开:隔一段时间试探性放少量请求,判断下游是否恢复

降级: 防的是"系统资源紧张时,核心功能也保不住"。

  • 比如大促时先保下单和支付,推荐、评论、积分展示可以先简化
  • 常见手段包括返回兜底值、关闭非核心模块、走简化流程

3. 三者怎么区分,怎么考

维度 限流 熔断 降级
主要防什么 流量过大 下游故障拖垮自己 资源紧张时保核心
典型动作 拒绝、排队、削峰 快速失败、暂时不再调用 关闭次要功能、返回兜底数据
真实场景 秒杀、热点接口 下游依赖超时 大促、故障应急

答题时可以用一句话概括:限流控流量,熔断断依赖,降级保核心。

七、微服务拆分原则

微服务不是拆得越细越先进。很多团队的问题不是不会拆,而是拆完之后发现调用链更长、接口更多、排障更难,结果从"一个大泥球"变成"一堆小泥球"。

怎么理解拆分原则:

  1. 围绕业务能力拆,不围绕技术层拆
    订单、库存、支付、会员是业务能力;Controller、Service、DAO 不是微服务边界。
  2. 服务内部高内聚,服务之间低耦合
    一个服务内部处理同一类业务变化,不要今天依赖库存、明天依赖营销、后天再依赖履约才能完成最基本动作。
  3. 数据自治
    每个服务最好拥有自己的数据边界,避免多个服务共同改一张核心表。
  4. 可独立部署
    如果改一个服务必须拉着五个服务一起发版,那说明边界还没真正分开。
  5. 粒度适中
    拆太粗,和单体没本质区别;拆太细,网络调用和治理成本会迅速上升。

行业里的经验判断: 当团队已经因为单体代码过大、发布互相影响、热点能力无法独立扩容而明显受限时,微服务拆分才真正有收益。如果这些问题还不明显,过早拆分往往是在提前引入复杂度。

八、答题模板

1. 概念解释题

[概念名称] 解决的是分布式系统中的 [核心矛盾] 问题。其核心思想是 [一句话概括机制] 。在工程上通常用于 [典型场景] ,主要优势是 [优点] ,代价是 [缺点]

2. 对比题

写对比题时,优先按这四个维度展开:

  • 一致性要求
  • 性能与吞吐
  • 实现复杂度
  • 适用业务场景

3. 选型题

对于题目中的 [业务场景] ,系统最主要的风险是 [例如不一致、流量冲击、重复执行、下游故障] 。因此更适合采用 [方案名称] 。原因是该方案能够在 [核心目标] 上更好满足要求,但需要注意 [工程代价或边界条件]

4. 常见卷面句式

  • 该方案优先保证一致性,因此会牺牲部分可用性或性能。
  • 该方案接受最终一致,以换取更好的吞吐和扩展性。
  • 该机制用于避免多实例并发修改同一资源。
  • 该治理手段的目标不是提升功能,而是防止故障扩散。
相关推荐
roman_日积跬步-终至千里9 小时前
【系统架构师案例题-知识点】数据库与缓存设计
数据库·缓存·系统架构
程序员大志11 小时前
系统架构设计师:最大流量问题
系统架构
池佳齐11 小时前
软考高级系统架构设计师备考(十九):数据库系统—数据库设计
数据库·系统架构
roman_日积跬步-终至千里12 小时前
【系统架构师案例题-知识点】系统建模
系统架构
roman_日积跬步-终至千里13 小时前
【案例题-知识点(2)】架构风格上(五大类详解)
数据库·架构·系统架构
下地种菜小叶14 小时前
电商系统架构总览怎么搭?一次讲清商品、库存、订单、支付、履约、营销的整体边界
系统架构
小夏子_riotous14 小时前
Docker学习路径——8、Dockerfile
linux·运维·docker·容器·系统架构·centos·运维开发
小江的记录本14 小时前
【微服务与云原生架构】Serverless架构、FaaS/BaaS、核心原理、优缺点
java·后端·微服务·云原生·架构·系统架构·serverless
黄昏回响16 小时前
信息系统基础知识(一):企业信息化与信息系统架构(下篇)
计算机网络·程序人生·系统架构·改行学it