专业书籍精读 · System Design Interview 卷一 · 第 2 章
System Design Interview: An Insider's Guide · Vol 1 · Ch 2 · Alex Xu · 2020
搬家前,你不会挨个称行李的重量,但你一定会先看一眼:这些东西,一辆小面包车装得下吗?看一眼就知道「装不下,得叫货车」——这个判断只花三秒,却决定了你今天要打哪个电话。这一章讲的就是工程师版的「看一眼」:在动手做任何系统之前,先用几个粗糙的数字判断出,这活儿到底是一辆小面包车的事,还是要叫车队。
这一章从头到尾没有一个精确答案。它算出来的每个数字都是拍的、圆整过的、四舍五入到「大概」的。可它恰恰是全书最有用的一章之一。因为决定架构长什么样的从来不是「3512 还是 4108」,而是「三千还是三百万」——差一千倍的两个世界,完全不该用同一套做法。
不做这一步的团队,会稳定地栽进两种坑,方向正好相反。
一种是高估。产品还没几个用户,团队已经按「以后会很大」的假想上了一整套复杂设施。结果是大半年时间花在维护这套设施上,产品本身几乎没动过。
另一种是低估。方案讲得挺漂亮,上线才发现要存的东西根本不是「买块硬盘」的量级,而是要塞满一整个仓库;等到发现时,改法就只剩下最贵最痛的那种了。
两种坑的共同点是:没人在开工前花三分钟,把大概的数量写在纸上。
① 先在脑子里备几把「标尺」。就像厨师不用量杯也知道「一撮盐大概多少」,工程师得先记住几个常识性的量:从记忆里取一样东西是「一眨眼」,走到楼下车库取是「一趟」,寄到地球另一边再寄回来是「一次跨国快递」。这三件事之间差着极大的倍数——记住这个差距,很多设计不用算就能一眼否掉。
② 把假设写下来。估算真正的产出不是那个数字,而是那串假设:有多少人在用、每人每天用几次、每次留下多大的东西、要留多久。写下来,别人才能指着其中一条说「这条我不同意」——于是讨论从「你猜我猜」变成了「我们改这一条再算一遍」。
③ 只求量级,大胆取整。把复杂的数一律圆成整数再算,算完只看结果落在哪一档。这不是偷懒,是因为你的输入本来就是拍的——用精确的算法去处理拍脑袋的输入,得到的只是看起来很精确的错误。
还有一个最容易被跳过的动作:算完平均,一定要再问一句「最挤的那一刻是多少」。一家饭馆按全天平均客流配座位,中午十二点一定会崩。真实世界里,最挤那一刻常常是平均的几十倍,而不是「多一点点」。
诚实说一句代价:这套办法只能告诉你在哪个数量级,它永远回答不了「到底能不能扛住」——那得靠真的压一遍。
估算不是为了算对,是为了在三分钟内排除掉那些一眼就不成立的方案。写下假设、大胆取整、只看量级,再补上一问:最挤的那一刻呢?
想进到具体机制、量级和示意图? → 切到精读版
你以为这一章在教「怎么把数算准」,其实它在教怎么用一串明说出来的假设,把一个开放问题压缩成一个量级。这一章的产出从来不是那个数字——是那串可以被别人当场推翻的假设。数字错一倍无所谓,量级错一千倍,你整个架构就建在了错误的前提上。
99.99%。p99 指第 99% 慢的那个请求的耗时——一百个请求里第 99 慢的那位等了多久。本章是卷一 Part I「方法与基础」的第二章,夹在两块之间:Ch1 给的是零件与施工顺序(加缓存、加从库、分片……),Ch3 给的是面试流程(澄清需求 → 高层设计 → 深挖 → 收尾)。而 Ch2 补上中间那块——凭什么判断现在该走到哪一步。它是全书最短的一章,却是后面十二个案例(限流器、短网址、YouTube、Google Drive……)每一章开头都要重跑一遍的固定动作。
面试官问「设计一个 X」,紧接着的第二句几乎必然是「大概多少用户、多少数据」。这不是刁难,是因为脱离量级谈架构没有意义:同一个功能,日请求一万次和日请求一百亿次,正确答案完全不同——前者一台机器加一个数据库就够了,后者要谈分片、多机房和对象存储。
不做估算会怎样?两个方向的失败都很典型。高估:产品只有五千用户,团队却上了分库分表加多活,八成时间在维护基础设施;而分片这一步几乎不可逆。低估:方案评审时说「存数据库里」,上线后才发现每天新增 30 TB——这不是加块盘的量级,是要重做整个存储层。
更隐蔽的第三种失败是只算了平均。一个日活 100 万、人均每天 10 次请求的服务,摊平后约 116 QPS,听起来轻松得很;但流量从来不是摊平的,晚八点那一刻可能是均值的几倍甚至几十倍。按平均配容量的系统,一定会在最需要它的那一刻挂掉。
所以这一章要建立的能力只有一个:给定几条关于用户行为的假设,在白板上、三分钟内、不用计算器,把 QPS、存储、带宽算到量级正确。
估算存储,永远是同一个乘法:一条记录多大 × 每天多少条 × 存多少天。所以第一件事是能在脑子里把数据量单位换算清楚。原书给的对照关系很朴素:一个 ASCII 字符占 1 字节;2^10 约等于一千,即 1 KB;2^20 约等于一百万,即 1 MB;2^30 约等于十亿,即 1 GB;2^40 是 1 TB;2^50 是 1 PB。
看着简单,落到实处就是判断力。一条推文的结构在书里被拆成:推文 ID 64 字节 + 正文 140 字节 + 媒体 1 MB。注意这三个数字之间的悬殊:文本部分只有两百字节,媒体是它的五千倍。这一眼就给出了整个存储设计的主线——文本随便放,媒体决定成本。真实系统里文本进数据库、媒体进对象存储加 CDN,根源就在这个比例上,而不是什么架构时尚。
第二张表是本章的灵魂,源自 Google 的 Jeff Dean,业内通称「每个程序员都该知道的延迟数字」。它的用法不是背诵,而是让你在听到一个设计时,立刻感知到它落在哪个数量级上。
原书从这张表里直接读出五条结论,每一条都是可操作的设计判据:内存快、磁盘慢;尽可能避免磁盘寻道;简单压缩很便宜(压 1 KB 只要 10 µs);跨网发数据前先压一下;数据中心通常分布在不同地区,跨区传输很花时间。
第三条和第四条合起来是一条很实用的推论:压缩 1 KB 花 10 µs,而跨洲往返要 150 ms——相差一万五千倍。所以「先压缩再发」几乎永远划算,这不是玄学,是把两个数字放一起就能看出来的算术。
第三张表把「高可用」这个形容词换算成具体的停机时间。它之所以重要,是因为多加一个 9 的成本通常是指数级上升的,而收益只是每月少停几分钟——值不值,得先把两边都摆成数字。
表 1 · 可用性目标 ↔ 允许停机时间(原书给的对照表,取常用四档)
| 可用性 | 每天允许停机 | 每月允许停机 | 每年允许停机 | 典型对应 |
|---|---|---|---|---|
99% | 14.40 分钟 | 7.31 小时 | 3.65 天 | 内部工具、非关键后台 |
99.9% | 1.44 分钟 | 43.83 分钟 | 8.77 小时 | 多数 SaaS 的默认承诺 |
99.99% | 8.64 秒 | 4.38 分钟 | 52.60 分钟 | 主流云服务的区域级 SLA |
99.999% | 864 毫秒 | 26.30 秒 | 5.26 分钟 | 电信 / 支付核心链路 |
把它读透的关键在最后两行的对比:从 99.9% 到 99.99%,你争取的是每月 39 分钟。这 39 分钟意味着一次人工介入的重启都来不及做——它必须靠自动故障转移,也就意味着冗余、健康检查、演练这一整套东西。原书特别点明:主流云厂商(Amazon、Google、Microsoft)给出的 SLA 通常在 99.9% 及以上,而且永远不会是 100%。
原书用一个例子把方法串起来。假设条件全部明说:3 亿月活;50% 日活;用户平均每天发 2 条推文;10% 的推文带媒体;数据保留 5 年。
拆开看,流量线只有两步:3 亿条除以一天的 86400 秒,约 3500 QPS;再乘 2 得峰值约 7000 QPS。存储线也只有两步:3 亿条里 10% 带媒体、每个 1 MB,得每天 30 TB;乘 5 年约 55 PB。
真正的价值在最后一步——把数字翻译成决定。7000 QPS 这个量级,说明单台数据库肯定不够,但也远没到「必须分片」的程度,先上缓存和读副本就能覆盖;而 55 PB 一出来,「媒体存数据库」这个选项当场出局,对象存储加 CDN 成为唯一合理解,且成本会盖过性能成为首要约束。这两句翻译,才是这一章要交付的东西。
上面那步「× 2 得峰值」,是整条推理链里最该被质疑的一环。原书取 2 是个方便的默认值,但流量曲线的形状是由业务形态决定的,不是由公式决定的。用 Twitter 自己公布过的真实数字看一眼就明白差距有多大。
所以这一步正确的做法不是换一个更大的常数,而是把它明确列为一条假设、并注明依据——「按我们过去一年的曲线,峰值约为均值的 6 倍」。有依据的 6 倍,比没依据的 2 倍或 25 倍都可靠。
原书最后给了三条实操技巧,看着朴素,每一条都在挡一类具体失误。
取整与近似。白板上不做复杂算术:99987 / 9.1 直接当成 100000 / 10 来算。这不是马虎——你的输入本来就是拍的假设,用高精度算法处理低精度输入,只会得到看起来很精确的错误。
写下你的假设。这是三条里最重要的一条,因为它改变了讨论的性质。假设写在白板上,评审就从「你猜我猜」变成了「把日活从 50% 改成 20% 再算一遍」。估算的输出是一条可复算的推理链,不是一个数。面试里也一样:面试官几乎不会纠正你的算术,但一定会挑战你的假设。
标注单位。只写 5 没有意义,要写 5 MB。原书把这条单独列出来,是因为单位混淆(把 MB 当 Mb、把每天当每秒)是估算里最常见也最致命的错误——它一次就能让你差 8 倍甚至 86400 倍。
原书还列了面试常考的五类估算:QPS、峰值 QPS、存储、缓存容量、机器台数。这五个几乎覆盖了所有「设计一个 X」题目的开场。
这一章的权衡不在「用哪个技术」,而在「算到什么精度就该停」和「算出来的量级对应哪一档架构」。
表 2 · 五类常考估算:怎么算、最容易错在哪
| 要估的量 | 怎么算 | 最常见的错 | 算完该问的下一句 |
|---|---|---|---|
| QPS | 日活 × 人均每日次数 ÷ 86400 | 把月活当日活用 | 读写比是多少? |
| 峰值 QPS | QPS × 峰值倍数(书里取 2) | 倍数拍太小;忽略突发事件 | 最坏的一天长什么样? |
| 存储 | 单条大小 × 每日条数 × 保留天数 | 漏算副本因子与索引开销 | 冷热怎么分层? |
| 缓存容量 | 热点数据占比 × 总量(常按二八法则取 20%) | 把全量数据都想放进内存 | 命中率目标是多少? |
| 机器台数 | 峰值 QPS ÷ 单机可承载 QPS,再留余量 | 按 100% 利用率算,不留冗余 | 挂一台 / 挂一个可用区还撑得住吗? |
表 3 · 算出的量级 → 该做什么(档位是业界通行经验值,非原书给出;具体阈值随硬件与业务差异很大)
| 峰值量级 | 通常够用的做法 | 代价 / 前提 |
|---|---|---|
| 约 100 QPS 以下 | 一台应用服务器 + 一个数据库 | 没有冗余——挂了就全站没了 |
| 约 1000 QPS 量级 | 负载均衡 + 多台无状态应用 + 读副本 | 多了一层要运维;复制滞后会读到旧数据 |
| 约 10000 QPS 量级 | 再加缓存层与 CDN;慢任务走队列 | 缓存一致性窗口;缓存挂掉会瞬间打死数据库 |
| 10 万 QPS 以上 / 数据装不下单机 | 分片;必要时多机房 | 近乎不可逆:跨片 join、热点、重分片全部变难 |
表 3 真正想说的不是这些数字,而是「量级 → 动作」这条映射本身应该在你脑子里存在。面试和架构评审里最见功力的一句话是:「按我刚才的假设算下来峰值大概是一万,所以这一档我上缓存和读副本就够,先不谈分片。」结论有没有挂在数字上,是新手和老手最明显的分水岭。
这套方法之所以值得单独一章,是因为它是唯一能把开放题收敛的工具。没有量级,「设计一个短网址服务」可以扯一小时;有了量级,讨论立刻收敛到「读写比 100:1、峰值一万 QPS、五年 3 TB」这几个具体约束上。下面几条不是书里说的,是相关公司和作者自己公开写的:
p99 延迟是 1 秒,那么一个需要扇出到 100 台服务器的请求,将有约 63% 会超过 1 秒。验证了「只估平均值会系统性低估痛苦」:规模越大,尾部越是那个决定用户体验的东西。J. Dean & L. A. Barroso「The Tail at Scale」, CACM 56(2), 2013 ↗99.99%,未达到则按档给服务抵扣。验证了「几个 9」不是形容词而是可核对、可索赔的承诺——你自己的 SLO 也应该这样定。Amazon Compute Service Level Agreement(官方 SLA 页)↗p99。扇出越多的架构,尾部被放大得越厉害(见上面 Tail at Scale 的 63%)。55 PB 是原始数据。真实集群通常存 3 份副本,再加索引、元数据、临时空间——落到采购单上往往是原始量的两三倍。这是估算里最常见、后果最贵的漏项。86400 倍。原书专门列一条讲标单位,不是啰嗦。10 ms)。今天的 NVMe SSD 随机读常在百微秒量级,把「磁盘慢」这一条压缩了约两个数量级。但表里真正有价值的是相对关系——内存 ≪ 本地盘 ≪ 同机房网络 ≪ 跨洲网络——这个次序至今没变,因为最后一格由光速决定。① 估算的产出不是数字,是一串可以被当场推翻的假设——写下来,讨论才有支点。
② 目标是量级正确:大胆取整、不用计算器;用精确算法处理拍脑袋的输入,只会得到精确的错误。
③ 三张底表要熟:2 的幂(容量换算)、延迟数字(Jeff Dean 那张)、可用性九位数。
④ 延迟表最该记住的是相对次序:内存 ≪ 本地盘 ≪ 同机房网络 ≪ 跨洲网络(150 ms,由光速兜底)。
⑤ 由此得的实用推论:先压缩再跨网发——压 1 KB 只要 10 µs,跨洲一次往返 150 ms,差一万五千倍。
⑥ 从 99.9% 到 99.99% 争的是每月 39 分钟,这点时间不够人工介入,只能靠自动故障转移——多一个 9 的成本是指数级的。
⑦ 完整流程记住 Twitter 例子:假设 → 3 亿条/天 → 3500 QPS / 峰值 7000 → 30 TB/天 → 5 年 55 PB。
⑧ 最后一步才是重点:把量级翻译成决定(55 PB ⇒ 媒体必须走对象存储 + CDN,成本盖过性能)。
⑨ 三条纪律:取整、写假设、标单位。单位混淆一次就能差 8 倍或 86400 倍。
⑩ 两个要带着怀疑读的地方:峰值系数别照抄 2(Twitter 是 25 倍),存储别忘乘副本因子。