想象一下,你是一家连锁咖啡品牌的运营负责人。老板把你叫到办公室,抛出一连串问题:北京新店开业,怎样保证从上海总店传过去的「海盐焦糖拿铁配方」一字不差?三家分店同时排队爆满,怎么安排人手才能让每个客人都不用等太久?有一家分店突然断电停业,怎么保证其他店不受牵连,客人还能喝上咖啡? 这一连串「开分店」的难题,恰恰就是分布式系统设计面试要考察的核心。你去面试 AI 工程师,面试官不会真让你开咖啡店,但他会问:系统要支撑 1 亿用户,数据库怎么分片?多个服务节点之间数据怎么保持一致?某个节点挂了,如何让整个系统不瘫痪?这些问题的本质,和「连锁咖啡店如何保证配方统一、顾客分流、分店容灾」完全同构——都是资源变多之后,如何让协作依然像一家店一样可靠。 这篇文章就用「经营连锁咖啡店」这个比喻,带你拆解分布式系统设计的六大高频考点:答题框架、CAP 定理、一致性协议、负载均衡、容错设计、高频题型套路。学完你就能在面试中,像资深的运营总监一样,不慌不忙地「开店」。

岗位关联

分布式系统设计面试考察的是架构思维和工程判断力,是 AI 工程师从「写单机代码」走向「设计大规模系统」的门槛。掌握这套答题框架,你的面试表现将与普通候选人拉开决定性差距。

目录
  1. 可视化演示
  2. 答题框架
  3. CAP定理
  4. 一致性协议
  5. 负载均衡
  6. 容错设计
  7. 高频题型
  8. AI 应用场景
  9. Python 演示
  10. 常见误区
  11. 面试题

可视化演示

展示系统处理流水线、实时性能指标曲线及多方案对比分析,包含数据流转、吞吐延迟监控与方案评估。

本节配有交互动效(进入视口后自动加载,约 3KB)。

系统可视化 处理流程:输入→处理→模型→输出 性能监控:实时指标 P=L×T

答题框架

开连锁咖啡店,第一步绝不是急着买咖啡机,而是先问清楚:铺面开在社区还是写字楼?主要做外带还是堂食?预估每天的客流量是多少?这些基础问题没想清楚,后面每一步都会返工。系统设计面试也一样——很多候选人一上来就画架构图,结果方向全错。面试官期待的第一步永远是「澄清需求」,就像优秀店长必须先搞清楚「这家店到底要服务谁」。

系统设计题在 AI 工程师面试中几乎是必考项,它没有标准答案,但有一套被广泛认可的「五步法」答题框架。这套框架的价值在于:它把「凭空设计一个大系统」的恐惧感,拆解成五个可以逐个击破的具体任务。面试官按步骤听下来,能直观感受到你的结构化思维,而结构化思维正是大规模系统设计最需要的能力。

五步法依次是:需求澄清、容量估算、核心架构、细节深化、扩展演进。需求澄清环节要主动提问,比如功能需求(读多写少还是写多读少)和非功能需求(可用性要求、延迟要求)。容量估算需要用数据说话——这一步是所有后续架构决策的依据,也是最容易让候选人暴露「工程直觉薄弱」的关键点,必须认真对待。

有了框架,胸中就有了地图。就像店长在开店前把「选哪条街、备多少货、招几个人」都想明白了,面试官把大问题抛给你时,你只需要按图索骥,一步步展开。这份从容,正是由「不是聪明,而是有章法」的专业感带来的。

S=N×D×T\displaystyle S = N \times D \times T
公式含义:系统需要的总存储量 S = 预计用户总数 N × 每个用户每天产生的数据量 D × 数据需要保留的天数 T。就像开店前计算「总共要备多少袋咖啡豆」——客人越多、每天消耗越大、备货周期越长,仓库就要越大。
  1. 用户总数 N首先估算系统上线后会有多少注册用户或日活用户,这是一切计算的起点。
    N=日活用户数×平均每用户关联数据条数\displaystyle N = \text{日活用户数} \times \text{平均每用户关联数据条数}
  2. 每日新增 D每个用户每天会产生多少新数据(比如发几条动态、上传几张图片),要量化到字节级别。
    D=单条数据平均大小×每用户每日动作次数\displaystyle D = \text{单条数据平均大小} \times \text{每用户每日动作次数}
  3. 保留周期 T数据需要保存多久(例如最近 90 天),超出部分是否归档或删除。
    T=数据保留天数\displaystyle T = \text{数据保留天数}
  4. 总存储量 S三者相乘得到容量下限,再乘以 2~3 的冗余系数作为设计余量。
    S=N×D×T\displaystyle S = N \times D \times T

CAP定理

连锁咖啡店的老板遇到了一个头疼的「不可能三角」:要保证北京、上海、广州三家分店的菜单和价格完全一致(一致性 C),要在客人进门瞬间立刻给出回应哪怕系统正在同步数据(可用性 A),要让每家分店在网络断开时也能独立营业靠着本地库存先卖起来(分区容错 P)。老板很快就想明白:网络断连是客观现实,不可能提前指望永远不断网;于是在「断网时是选择先让客人等着保证数据统一,还是先接待客人过后再同步数据」之间,必须做一个痛苦但清醒的取舍。

这个「对分布式系统在不可靠网络下权衡取舍」的约束,就是 CAP 定理。C(Consistency)指所有节点在同一时间看到同样的数据;A(Availability)指每个请求都能在有限时间内收到非错误的响应;P(Partition Tolerance)指系统允许节点间通信中断但仍然继续运行。CAP 的核心洞察是:网络分区(P)在真实世界中无法避免,所以设计者真正面临的选择只有 CP(牺牲部分可用性保一致)或 AP(牺牲一致保可用)两条路。

AI 工程师面试中,最常见的就是「面试官抛出一个系统,让你判断该选 CP 还是 AP」。判断依据其实很朴素:如果数据错了会有资金损失(如支付系统、库存扣减),就选 CP;如果数据稍微滞后但服务不能断(如朋友圈点赞数、用户位置更新),就选 AP。这个看似简单的取舍,恰恰是架构师核心判断力的体现,也是面试官最想听到「有依据的推理」而不是「背熟的答案」。

「不可能三角」不是让人绝望的,而是帮人做清醒的选择。优秀的店长不会奢望每家店永远同步又能永远即时服务,而是会告诉老板:我们的核心目标是咖啡不错卖、账不能错,所以网络抖动时宁可让客人多等两秒也要保证数据库一致。在面试中,你能说出这种「我清楚代价,我做了取舍」的话,就已经超出大多数只会背定义的候选人了。

Availability=UptimeUptime+Downtime\displaystyle \text{Availability} = \frac{Uptime}{Uptime + Downtime}
公式含义:系统的可用性等于「正常运行时间」除以「运行总时间」。比如一年 8760 小时中只宕机了 8.76 小时,可用性就是 99.9%,绰号「三个九」。这是面试中衡量「高可用」的量化语言。
  1. 正常运行时间 Uptime系统对外提供正确服务的时间累计值。
    Uptime=总时间−故障停机时间\displaystyle Uptime = \text{总时间} - \text{故障停机时间}
  2. 停机时间 Downtime系统因故障、升级、断网而无法服务的时间累计值。
    Downtime=∑每次故障持续时间\displaystyle Downtime = \sum \text{每次故障持续时间}
  3. 可用性比值分子是好的时间,分母是总时间,比值越接近 1 说明系统越可靠。
    Availability=UptimeUptime+Downtime\displaystyle \text{Availability} = \frac{Uptime}{Uptime + Downtime}

一致性协议

总店研发了一款「桂花冷萃」新配方,要同步给全国 50 家分店。负责人发现一个难题:如果逐一打电话确认,有的分店店长正好在忙、有的信号不好,总不能等 50 家全部确认后再统一上新吧?于是决定:只要超过一半的店长回电话确认「收到」,配方就算正式生效。剩下没接电话的店,等他们忙完了再补发确认——这样既不会因为一家店失联就推迟整个上新计划,也能保证大多数店遵守同一套配方。

这个故事讲的是分布式系统中最经典的「共识问题」(Consensus),而 Raft 协议就是解决这个问题的著名算法之一。Raft 的核心机制有四个关键概念:Leader 选举(大家在节点中投票选出一个「总店长」)、日志复制(所有写操作先经过 Leader 记录,再下发给 Follower)、多数派提交(超过半数节点确认才真正生效)、脑裂防护(用任期号和多数票避免出现两个「总店长」)。

面试中常考的一个细节就是「多数派」到底是多少。共识算法不需要所有节点同意,只需要「超过一半」即可。为什么是超过一半而不是全部?因为任意两个多数派集合必然有交集,不会出现「两拨节点各自做了不同决定」的分裂局面。这个数学性质,是整个共识算法的安全基石。很多候选人能说出 Raft 四个字,却解释不了为什么是多数派——差的就是这一层深入理解。

分店配方的同步终于有了稳妥的机制。当面试官问起「Raft 怎么保证一致性」时,你可以用这个「总店长 + 多数派确认」的故事娓娓道来:先选一个总店长,所有配方改动都经由总店长统一下发,只要超过半数分店确认就生效。面试官一听就懂,也会觉得你是真的理解共识的本质,而不只是在背协议名词。

Q=⌊N2⌋+1\displaystyle Q = \left\lfloor \frac{N}{2} \right\rfloor + 1
公式含义:N 个节点组成的集群中,需要至少 Q 个节点达成一致,才算做出有效决策。N=5 时 Q=3,N=7 时 Q=4。这就像 50 家分店确认 26 家即可上新——多一票都不需要。
  1. 计算半数节点总数 N 除以 2,向下取整得到「一半」的近似值。
    ⌊N2⌋=不超过 N/2 的最大整数\displaystyle \left\lfloor \frac{N}{2} \right\rfloor = \text{不超过 } N/2 \text{ 的最大整数}
  2. 加一形成多数在半数基础上加 1,得到严格超过半数的节点数。
    Q=⌊N2⌋+1\displaystyle Q = \left\lfloor \frac{N}{2} \right\rfloor + 1
  3. 数学保证任意两个包含 Q 个节点的集合,必然存在至少一个共同节点(因为 2Q > N)。
    2Q=2⌊N2⌋+2>N\displaystyle 2Q = 2\left\lfloor \frac{N}{2} \right\rfloor + 2 > N

负载均衡

咖啡店的排队区经常挤满客人。聪明的店长安排了一位引导员,专门站在门口观察:一号收银台排队最长的就引导新客人去二号,二号也忙就让新客人去三号。引导员的存在,让三家分店的接待量被均匀摊开,不会有哪家店忙到崩溃而另一家闲着。在分布式系统里,这个「引导员」就是负载均衡器(Load Balancer),它前端接收所有请求,再根据策略分发给后台的服务器集群。

负载均衡面试中会考察三类核心问题,每一类都对应一种「引导员策略」。轮询策略是最朴素的「挨个叫号」——请求按顺序轮流分配,实现简单但没考虑到后端机器的性能差异;最少连接策略更像聪明的引导员——实时盯着每台服务器的当前连接数,谁闲就把请求给谁;一致性哈希策略则多用于缓存场景——让同一个用户的请求总被路由到同一台服务器,从而命中本地缓存。

面试高频追问是「引导员自己挂了怎么办」以及「怎么发现某台服务器已经坏了」。前者靠负载均衡器自身做集群(两台引导员做心跳互备),后者靠健康检查——负载均衡器定期向服务器发探测请求(ping 一个固定端口),连续几次没响应就把该服务器从候选列表里摘除。这套「探活 + 摘除」的思维,在系统设计中无处不在,属于必须张口就来的基本功。

有了引导员,连锁店的服务质量大大提升。面试官如果追问「你负责的系统流量涨了十倍怎么办」,你的第一反应不应该是「加机器」,而应该是「加引导员 + 设计更好的分流策略」。从排队引导员的角度去理解负载均衡,你会发现它根本不是高深的技术名词,而是一套朴素的「让闲的人多干、让忙的人喘气」的协作智慧。

Starget=arg⁡min⁡i  Li\displaystyle S_{target} = \arg\min_{i} \; L_i
公式含义:在所有候选服务器中,挑选当前连接数 L_i 最小的那一台作为目标服务器 S_target。这就是最少连接策略的数学表达。
  1. 列出连接数每台服务器都有一个当前活跃连接数记录,记为 L_1, L_2, ……, L_n。
    Li=服务器 i 的当前连接数\displaystyle L_i = \text{服务器 } i \text{ 的当前连接数}
  2. 寻找最小值遍历所有服务器,找到连接数最小的那台,用 argmin 表示「使 L_i 最小的下标 i」。
    arg⁡min⁡iLi=取得最小 Li 的服务器编号\displaystyle \arg\min_{i} L_i = \text{取得最小 } L_i \text{ 的服务器编号}
  3. 路由目标将本次请求分配给该服务器。
    Starget=当前最空闲的服务器\displaystyle S_{target} = \text{当前最空闲的服务器}

容错设计

开店最怕什么?咖啡机突然坏了、店员临时请假、水管爆了。成熟的连锁品牌绝不会只有一台咖啡机——总是会多备一台封存的备用机,每周试运行一次;也不会让一个店长全权负责所有事务,总是有副店长能随时顶上。这套「多备一份、随时能换」的思路,在分布式系统里被称为容错设计(Fault Tolerance),其核心哲学是:故障一定会发生,与其祈求不发生,不如设计让系统「坏了一部分也能继续跑」。

容错设计的四大常用手段,每一个都能在咖啡店找到对应。冗余备份:数据或服务节点保留多份副本,一台机器出问题,其他副本继续服务;故障转移:监控到主节点心跳丢失,自动把流量切到备节点,就像副店长自动顶上;超时重试:客户端请求没收到响应,过一段时间自动重发,就像客人没等到咖啡会再催一次;幂等设计:对同一个请求,无论执行多少次,结果都和执行一次一样,防止重复扣款、重复下单的灾难。

面试中,幂等设计是容错方向的「最高频考点」。很多候选人不理解为什么需要幂等——因为只要有了超时重试,同一个请求就可能被发送多次;如果接收方每次都「重新执行」,就会产生重复数据。解决办法是给每个请求分配唯一 ID(requestId),接收方在数据库里记录已处理的 ID,重复请求直接返回之前的结果。这套「记录已处理状态」的思想,本质上和「客人的取餐号」一模一样——凭号取餐,取过一次就作废,再拿同样的号也不会再给你一份。

容错设计的核心不是「硬件绝对不出错」,而是「接受出错,但让错误不扩散」。就像咖啡店从不指望咖啡机永远不坏,而是用备用机、副店长、取餐号这些精妙的小机制,让任何突发状况都变成「日常运营的一部分」。面试时主动说出这层「拥抱故障」的心态,比堆砌一堆容错术语高级得多。

Rsystem=1−∏i=1n(1−Ri)\displaystyle R_{system} = 1 - \prod_{i=1}^{n} (1 - R_i)
公式含义:n 个独立的备份组件并联成一个系统时,系统整体可用性 R_system = 1 − 所有组件同时故障的概率。哪怕单个组件可用性只有 90%,两个备份并联后整体可用性就能提升到 99%。
  1. 单组件故障率第 i 个组件的可用性是 R_i,它发生故障的概率就是 1 − R_i。
    P(组件 i 故障)=1−Ri\displaystyle P(\text{组件 i 故障}) = 1 - R_i
  2. 全部故障概率n 个独立组件同时故障的概率是各自故障率的乘积(因为需要所有备份都坏掉系统才不可用)。
    P(全部故障)=∏i=1n(1−Ri)\displaystyle P(\text{全部故障}) = \prod_{i=1}^{n} (1 - R_i)
  3. 整体可用性系统可用的概率 = 1 − 全部故障的概率。
    Rsystem=1−∏i=1n(1−Ri)\displaystyle R_{system} = 1 - \prod_{i=1}^{n} (1 - R_i)

高频题型

新店开业前,老练的店长会做一次「模拟演练」:假设下午三点客流高峰,收银台爆满、咖啡机排长队、外卖订单翻倍——每一步该怎么做,全部提前预演一遍。面试前的系统设计准备也是如此,面试官翻来覆去考的就是那么几种经典场景,每一种都有相对固定的套路。提前「演练」过这些题型,考场上的表现会完全不一样。

面试中最常见的四种「高频题型」,每一个都有它的核心考察点。URL 短链服务(设计 bit.ly)考察的是哈希取模 + 发号器两种方案的选择,以及 302 重定向的细节;消息队列考察的是「削峰填谷」的异步解耦思想,核心组件是 Broker、Producer、Consumer;秒杀系统考察的是「缓存 + 限流 + 异步下单」三件套,重点在于防线前置,把流量挡在数据库之前;排行榜考察的是 Redis ZSet 或跳表的有序结构,核心权衡是读写性能的取舍。

这类题目背后有一套万能公式:先澄清需求(读多还是写多?秒杀就是典型写量大爆发),再估算峰值 QPS(每秒请求数),接着画核心架构(客户端 → 负载均衡 → 应用层 → 缓存 → 数据库),最后针对瓶颈做细化。比如设计短链服务时,一旦估算出「每天新增 100 万条长链接、读请求 QPS 约 5000」,方案自然就清晰了——用发号器生成唯一 ID,再转成 62 进制字符串,缓存热点短链。

有了这套「模拟演练」,面试就变成了一场可预测的实战。就像店长把高峰期的每个细节都刻在心里,面试官说出「设计一个秒杀系统」时,你脑海中会立刻浮现出完整链路:流量进来先过负载均衡,再到缓存层挡一波,真正的订单通过消息队列异步落库。所有动作行云流水,而这正是「高频题型套路」带给你的底气。

QPS=U×R86400\displaystyle \text{QPS} = \frac{U \times R}{86400}
公式含义:QPS(每秒查询数)≈ 日活用户数 U × 平均每用户每天请求次数 R,再除以一天的秒数 86400。这是系统设计面试中最常用的估算公式,用来回答「系统要承受多大流量」这类问题。
  1. 日活用户 U系统每天实际活跃使用的用户数量,不是注册总量,通常用峰值日活来算更保险。
    U=DAU(日活跃用户数)\displaystyle U = \text{DAU(日活跃用户数)}
  2. 人均请求数 R每个活跃用户平均每天发出的请求次数,可以从产品行为去估算(如平均刷 20 次朋友圈 = 20 次请求)。
    R=每人每日平均请求数\displaystyle R = \text{每人每日平均请求数}
  3. 一天的秒数 86400把「每日总量」除以「一天的秒数」,就得到每秒平均请求数。
    QPS=U×R86400\displaystyle \text{QPS} = \frac{U \times R}{86400}

AI 应用场景

场景一:AI面试模拟助手的「系统设计」智能陪练

阶段:面试准备与模拟训练

解决的问题:多数候选人在系统设计面试时缺乏实战演练机会,无法获得结构化的反馈。AI 面试模拟助手可以扮演面试官,按五步法框架自动提问,并对候选人的回答进行结构化评估。

具体案例:某 AI 面试训练平台内置「系统设计陪练」功能。候选人输入「设计一个类似微信朋友圈的 feed 流」,AI 助手按「需求澄清 → 容量估算 → 架构设计 → 细节追问」的顺序逐步提问。候选人的回答被语音转文字后,AI 用负载均衡、CAP 取舍、缓存策略等维度打分,并指出「你跳过了容量估算,请先估算 QPS」。这种即时陪练让候选人能在真实面试前完成 10 次以上高质量模拟。

场景二:AI 推荐系统的分布式特征服务

阶段:模型推理与在线服务

解决的问题:推荐系统需要实时读取用户特征和物品特征,这些都是分布在不同集群上的数据。如果特征服务不可用,整个推荐链路就会瘫痪,因此必须做容错和负载均衡。

具体案例:某头部电商的推荐系统,特征服务由 200 台机器组成集群,前面部署了 4 个负载均衡器。每天的峰值请求 QPS 达到 80 万。当流量高峰来临时,负载均衡器通过「最少连接」策略把请求均匀分发到各特征服务节点;当某台机器 CPU 飙高、健康检查连续失败时,自动从集群中摘除该节点。同时特征数据在三个机房各存一份副本,单机房故障时流量自动切到另外两个机房——这背后正是冗余备份与故障转移的实践。

场景三:大模型训练平台的故障自动恢复

阶段:模型训练与容错管理

解决的问题:大语言模型的训练动辄需要上千张 GPU,训练时长达数周。任何一台 GPU 故障都会导致整个训练任务中断,如果不做容错设计,一次训练可能白跑几天。

具体案例:某 AI 公司训练千亿参数大模型时,使用了「异步检查点 + 节点健康检测」机制。每 10 分钟保存一次模型权重到分布式文件系统,同时监控每个 GPU 节点的健康状态。当检测到某个节点温度过高或通信超时,调度系统自动将该节点的训练任务迁移到备用节点,并从最近的检查点恢复训练。整次训练中发生了 17 次节点故障,但训练任务从未中断,只额外损失了 8 分钟的重启时间——这就是「拥抱故障」的容错设计在 AI 工程中的典型应用。

Python 演示

python66 行
import random  class Server:    def __init__(self, name, capacity):        self.name = name        self.capacity = capacity        self.connections = 0     def accept(self, request):        if self.connections >= self.capacity:            return False, f"{self.name} 已超载,拒绝 {request}"        self.connections += 1        return True, f"{self.name} 处理 {request}"     def release(self):        if self.connections > 0:            self.connections -= 1  class LoadBalancer:    def __init__(self, servers):        self.servers = servers        self.cursor = 0     def round_robin(self, request):        server = self.servers[self.cursor]        self.cursor = (self.cursor + 1) % len(self.servers)        accepted, msg = server.accept(request)        return msg     def least_connections(self, request):        server = min(self.servers, key=lambda s: s.connections)        accepted, msg = server.accept(request)        return msg  def simulate():    servers = [        Server("Server-A", 3),        Server("Server-B", 2),        Server("Server-C", 4),    ]    lb = LoadBalancer(servers)    requests = [f"req-{i}" for i in range(1, 11)]     print("=== 轮询策略 ===")    lb.cursor = 0    for s in servers:        s.connections = 0    for req in requests:        print(lb.round_robin(req))        if random.random() < 0.4:            random.choice(servers).release()     print("\n=== 最少连接策略 ===")    for s in servers:        s.connections = 0    for req in requests:        print(lb.least_connections(req))        if random.random() < 0.4:            random.choice(servers).release()  if __name__ == "__main__":    simulate()
目的:演示两种负载均衡策略的差异。轮询策略不管服务器当前状态,按顺序挨个分发;最少连接策略则实时检查每台服务器的连接数,始终选择最空闲的服务器处理新请求。
  1. class Server:定义一台服务器,每个实例有自己的名称、容量上限和当前连接数。
  2. def accept(self, request):服务器接收请求,如果连接数已达容量上限则拒绝,否则连接数加一并返回处理成功的消息。
  3. class LoadBalancer:负载均衡器,内部维护一个服务器列表和一个轮询游标。
  4. def round_robin(self, request):轮询策略:游标按顺序移动,请求均匀地轮流分配给每台服务器。
  5. self.cursor = (self.cursor + 1) % len(self.servers)游标走到末尾后取模回到开头,实现循环分发。
  6. def least_connections(self, request):最少连接策略:用 min() 按 connections 字段选出连接数最少的服务器。
  7. lambda s: s.connections匿名函数,告诉 min() 按每台服务器的连接数作为比较依据。
  8. for s in servers: s.connections = 0模拟开始时重置所有服务器的连接数,保证两种策略对比不受初始状态影响。
  9. if random.random() < 0.4: random.choice(servers).release()模拟部分请求处理完成,随机释放服务器上的连接,让负载动态变化。

常见误区

误区CAP 定理说的不就是 C、A、P 三个性质里随便选两个吗?那我优先舍弃 P 不就行了,为什么分布式系统都要提 P?

正解分布式环境下,节点间的网络分区是物理现实,你『舍弃 P』的后果是:一旦断网,整个系统直接停止服务,这在分布式系统中等于宣布系统不可用。所以 P 是必须保留的底座,真正的选择只在 C 和 A 之间。试想连锁咖啡店不可能通过「把所有分店搬进同一栋楼」来避免断网,它只能接受网络可能断掉,然后在断网时决定「让客人等」还是「先接待再同步」。

为何常见:因为 CAP 在书籍中常被简化为「三选二」的三角形图示,误导读者以为三个可以任意取舍,忽略了「P 在分布式环境中的不可回避性」。

误区数据越一致越好,能选强一致选强一致,凭什么有人说最终一致性更合适?

正解强一致性的代价是性能下降和可用性受损。以连锁咖啡店为例,如果每次卖咖啡前都必须确认全国所有分店的库存都同步了,客人等一杯咖啡的时间会从 2 分钟变成 20 秒甚至 2 分钟。对「朋友圈点赞数」「直播间热度」这类数据,最终一致性意味着几秒延迟完全不影响用户体验,却能换来高并发和低延迟。技术选型没有绝对优劣,只有适不适合当前业务场景。

为何常见:人对「一致性」有天然的追求倾向,总觉得最终一致是「不对的」,忽略了分布式系统本质上是拿一致性换性能的工程妥协。

误区负载均衡器的作用就是「轮流分发」,或者随机挑一台服务器把请求甩过去,没什么技术含量。

正解真实系统里的负载均衡要考虑很多约束:服务器性能权重(8 核的机器应该比 4 核的接更多请求)、会话保持(同一个用户的请求要路由到同一台机器,否则登录态就丢了)、健康检查(发现某台机器没响应就自动摘除)。它更像是咖啡店的引导员,不只是「挨个叫号」,还要顾全熟客偏好、关注哪台收银机快、哪台收银机坏了。面试中能说出「健康检查 + 权重 + 会话保持」这三板斧,才算是真正理解负载均衡。

为何常见:因为入门教程通常先讲最简单的轮询策略,让学习者形成「负载均衡就这」的刻板印象,忽略了生产环境中的工程复杂度。

误区系统设计面试是不是有「标准答案」?我是不是应该背下常见系统设计的「最优架构」?

正解系统设计题恰恰没有标准答案,面试官考察的是你的「思考过程」和「权衡能力」。就像问「这家咖啡店该怎么开」,正确答案取决于选址、预算、目标客群——不同约束下最优方案完全不同。面试官给你一分半钟思考,期待的是你能主动澄清需求、做出合理假设、给出有依据的取舍,而不是像复读机一样输出一套固定架构。能说清「为什么选这个方案、它的代价是什么」的候选人,远比「背了一个看起来很牛的方案」的候选人得分高。

为何常见:应试教育养成了找「标准答案」的思维惯性,很多候选人误以为面试就是「猜中面试官心里的答案」,忽视了架构设计本质上是一个多目标优化问题。

误区Raft 协议要求全部 Follower 节点确认日志后才算提交,否则数据就不安全,少一个节点确认都不能提交。

正解Raft 等共识算法采用「多数派确认」而非「全量确认」,只需要超过一半的节点写入成功即可提交。这个设计的精妙之处在于:任意两个多数派必然有交集,因此不会出现「两个 Leader 各做各的决定」的脑裂问题。现实中,5 个节点中 3 个确认即可提交日志,留下 2 个节点缓慢同步并不会阻塞整个系统。这也是为什么多数派 Q = ⌊N/2⌋ + 1 这个公式是共识算法的灵魂——不是「越安全越好」,而是「数学上恰好够安全」。

为何常见:直觉上「确认的人越多越安全」似乎更合理,很多人没有理解多数派背后的数学保证,也没意识到「全量确认」会导致系统可用性急剧下降。

面试题

Q1. 在系统设计面试中,候选人收到题目后首先应该做什么?

单选题 · easy

  • A.立即画出系统架构图
  • B.主动澄清功能需求和非功能需求← 正确答案
  • C.直接开始估算服务器的数量
  • D.说出自己熟悉的某个开源方案

答案:B。系统设计面试第一步永远是需求澄清。就像开咖啡店前先搞清楚开在哪里、服务谁一样,不问清楚需求就画架构图,很可能整个方案都建立在错误的假设之上。B 是标准的第一步,A、C、D 都跳过了最关键的「对齐问题」环节。

Q2. CAP 定理中,当网络分区发生时,系统必须在哪两个性质之间做出取舍?

单选题 · mid

  • A.一致性和分区容错
  • B.可用性和分区容错
  • C.一致性和可用性← 正确答案
  • D.分区容错和线性扩展

答案:C。P(分区容错)在分布式系统中是必须保留的底座,因为网络断连无法避免。真正的取舍发生在 C(一致性)和 A(可用性)之间:分区发生时,是选择停止服务等待数据统一(CP),还是继续服务但数据可能暂时不一致(AP)。C 和 B 都误解了 P 的必然性,D 的「线性扩展」根本不属于 CAP 的范畴。

Q3. 以下哪些是 Raft 共识算法中的核心角色或机制?

多选题 · mid

  • A.Leader 负责接收写请求并同步日志← 正确答案
  • B.Follower 只能被动接受 Leader 的日志← 正确答案
  • C.候选者可以在选举超时后发起新一轮投票← 正确答案
  • D.每次写入需要所有节点确认提交

答案:ABC。Raft 将节点分为 Leader、Follower、Candidate 三种角色。Leader 接收客户端写请求并复制日志,Follower 被动同步,Candidate 在选举超时后发起新选举,A/B/C 都正确。D 错误,因为 Raft 采用多数派确认而非全量确认,只需超过半数节点写入成功即可提交日志。

Q4. 最少连接负载均衡策略的核心思想是什么?

单选题 · mid

  • A.将请求轮流分配给每台服务器
  • B.优先将请求分配给当前连接数最少的服务器← 正确答案
  • C.将请求均分给权重最高的服务器
  • D.根据用户 IP 哈希结果选择固定服务器

答案:B。最少连接策略就是「引导员观察谁最闲就让谁接客」——实时统计每台服务器的活跃连接数,选择最小的那台分发请求。A 是轮询策略,C 是加权轮询,D 是 IP 哈希,三者都不是基于「实时负载」的决策方式。

Q5. 关于 Raft 的多数派提交机制,下列说法正确的有哪些?

多选题 · hard

  • A.Q = ⌊N/2⌋ + 1 是达成共识所需的最少节点数← 正确答案
  • B.多数派机制保证任意两个已提交的决策不会互相矛盾← 正确答案
  • C.少数派节点可以继续选出一个新 Leader 并提交日志
  • D.多数派确认既能保证安全也能提升系统可用性← 正确答案

答案:ABD。A 正确,多数派的确切公式是半数加一。B 正确,因为任意两个多数派集合必有交集,不可能出现两边各自提交互相矛盾的日志。D 正确,相比全量确认,多数派机制让系统在少量节点失联时仍能正常工作。C 错误,少数派(不足半数)无法选举出合法 Leader,也无法提交任何日志。

Q6. 幂等设计解决的是什么问题?

单选题 · easy

  • A.服务器宕机后数据丢失问题
  • B.同一个请求被重复执行导致数据不一致的问题← 正确答案
  • C.多台服务器数据同步延迟的问题
  • D.数据库连接池耗尽的问题

答案:B。超时重试机制导致同一个请求可能被发送多次。如果没有幂等设计,重复下单、重复扣款等事故就会发生。幂等设计通过 requestId 去重,保证「同一请求执行 N 次和执行 1 次结果相同」。A 是冗余备份解决的问题,C 是一致性协议解决的问题,D 是连接池管理问题。

Q7. 设计 URL 短链服务时,下列哪种方案能保证短码唯一且不可预测?

单选题 · mid

  • A.直接对长 URL 取 MD5 哈希并截取前 6 位
  • B.使用发号器生成自增 ID,再编码为 62 进制字符串← 正确答案
  • C.使用随机数生成 6 位短码并存入缓存
  • D.使用原 URL 的长度作为短码

答案:B。发号器生成全局唯一自增 ID,再转为 62 进制短字符串,天然保证唯一性和不可预测性,是生产环境常用的方案。A 的哈希截断可能产生碰撞,C 的随机数也无法绝对避免冲突,D 完全不可行(不同 URL 可能长度相同)。面试中能说出「发号器方案」是很加分的。

Q8. 以下哪些属于分布式系统容错设计中的常用手段?

多选题 · mid

  • A.数据多副本冗余存储← 正确答案
  • B.主节点故障后自动切换到备节点← 正确答案
  • C.客户端请求设置超时并自动重试← 正确答案
  • D.使用更强性能的 CPU 避免故障

答案:ABCE。A 是冗余备份,B 是故障转移,C 是超时重试,E 是幂等设计,它们都是教科书级的容错手段。D 是错误的——换更强的 CPU 只能降低性能瓶颈概率,但无法消除故障;容错设计的前提就是「故障必然发生」,而不是「用硬件消除故障」。

Q9. 系统设计面试中,容量估算公式「QPS = 日活用户数 × 人均请求数 ÷ 86400」算出的结果是?

单选题 · easy

  • A.每秒的平均请求数← 正确答案
  • B.每天的平均活跃用户数
  • C.每台服务器的最大连接数
  • D.数据库的索引深度

答案:A。这个公式把「每日总请求量」除以「一天的秒数 86400」,得到的正是每秒平均请求数(QPS)。它用于回答「系统上线后大概要承受多大流量」这个关键问题,也是后续架构选型的依据。B、C、D 都偏离了 QPS 的定义。

Q10. 关于负载均衡的健康检查机制,下列说法正确的有哪些?

多选题 · hard

  • A.负载均衡器通过定期发送探测请求来确认服务器存活← 正确答案
  • B.健康检查连续失败多次后,服务器会从候选列表中被摘除← 正确答案
  • C.恢复健康的服务器可以自动重新加入负载均衡池← 正确答案
  • D.健康检查只能检查 HTTP 端口,无法覆盖其他协议

答案:ABC。A 是健康检查的基本原理,B 描述摘除机制正确,C 描述恢复机制正确。D 错误,健康检查支持 TCP/UDP/HTTP 等多种协议形式,并不局限于 HTTP 端口。健康检查是「引导员发现某台收银机坏了就暂停派活给它」的技术化实现,它保证了负载均衡器不会把请求发给一台已经挂掉的服务器。