Day 26 Medium Capacity Estimation Back-of-Envelope QPS / Storage / Bandwidth

容量估算 — 用一张草稿纸把『这个设计扛不扛得住』算出来Capacity Estimation: QPS, Storage, Bandwidth & the Discipline of Assumptions

问题场景 + 需求约束

面试官说:「设计一个 1 亿 DAU 的图文 Feed(类 Instagram)。」在画任何方框之前,你得先回答一个问题:这套系统是一台机器的活,还是一千台机器的活? 答案决定架构形态——单库还是分片、同步 fanout 还是异步、缓存放多大。容量估算(capacity estimation / back-of-the-envelope)就是用几个假设和数量级常数,在 2 分钟内把 QPS、存储、带宽、内存推算到正确的 10 倍以内,从而把无数种架构砍到一两种。

它的目的不是精确——精确是压测和真实指标的事。它的目的是排除明显不可行的设计:如果算出来写入要 200 GB/s 带宽,那「单机 + 本地磁盘」的方案当场出局。先固定本期的输入假设:

高层架构(估算的推导链)

容量估算不是一团数字,而是一条有向的推导链:所有量都从 DAU 和「人均行为」两个根假设流出。下图是这条链——每个箭头是一次乘除,记住链路就不会漏算。

graph TD
    DAU["DAU = 1亿
+ 人均行为假设"] W["写请求/天
2亿"] R["读请求/天
50亿"] AQ["平均 QPS
÷ 1e5 s/day"] PQ["峰值 QPS
× 峰值因子 2~3"] ST["存储
writes × 对象大小 × 留存"] BW["带宽
QPS × payload"] MEM["缓存内存
热数据 × 80/20"] DAU --> W --> AQ DAU --> R --> AQ AQ --> PQ W --> ST R --> BW R --> MEM classDef root fill:#2a1530,stroke:#ff7ab6,color:#e8eef5 classDef mid fill:#1a2530,stroke:#64c8ff,color:#e8eef5 classDef out fill:#0e2030,stroke:#5eead4,color:#e8eef5 class DAU root class W,R,AQ mid class PQ,ST,BW,MEM out

两个根假设(规模 + 人均行为)决定一切;QPS 决定算力,存储/带宽/内存决定形态

关键技术点

1. 数量级直觉:2 的幂、时间常数与延迟数字

核心 trade-off:记住一小撮常数 → 心算速度,换取 ±2 倍以内的误差。 估算的地基是三组「免背公式」的数字。第一组是 2 的幂与数据单位2^10≈10^3=KB2^20=MB2^30=GB2^40=TB2^50=PB。第二组是 时间常数:一天 ≈ 86400 ≈ 10^5 秒(这个近似让「每天 N 次」直接除以 10 万得 QPS,是估算里用得最多的一步)。第三组是 Jeff Dean 的延迟数字:它告诉你哪一步是瓶颈。

操作量级含义
L1 / 内存引用~1 ns / ~100 nsCPU 本地,几乎免费
内存顺序读 1 MB~微秒级(µs)内存带宽很大
SSD 随机读~16 µs比内存慢百倍
同数据中心往返 (RTT)~0.5 ms一次 RPC 的下限
磁盘寻道 / SSD 顺序读 1 MB~ms 级批量优于随机
跨洲往返 (CA↔NL)~150 ms光速决定,无法优化
为什么有用:p99 < 200 ms 的 feed 里,跨洲一次 RTT 就吃掉 150 ms——所以必须就近部署、不能让请求绕地球。延迟数字让你一眼看出哪个设计在物理上就不可能
现实案例:Jeff Dean 在 Google 的工程文化里把这套数字立为基准(Latency Numbers Every Programmer Should Know),Colin Scott 还做了「随年份变化」的可视化版。ByteByteGo(Alex Xu《System Design Interview》第 2 章)把「2 的幂 + 可用性 nines + QPS 推导」整理成面试速查表。

2. 从 DAU 推 QPS:平均、峰值与读写分离

核心 trade-off:用平均 QPS 估算简单,但按平均部署的系统会在高峰当场挂掉。 推导分三步:① 每天请求数 = DAU × 人均次数;② 平均 QPS = 每天请求数 ÷ 10^5;③ 峰值 QPS = 平均 × 峰值因子。峰值因子来自流量的日内不均匀——晚高峰、午休、突发热点,社交类常取 2~3×,秒杀/直播开播可达 10× 以上。

# 本期数字(1 亿 DAU)
写: 1e8 × 2  = 2e8  /day → 平均 2000 QPS  → 峰值 ~5k  写 QPS
读: 1e8 × 50 = 5e9  /day → 平均 5万 QPS   → 峰值 ~10万 读 QPS
读写比 ≈ 25:1  →  读路径才是设计重心(缓存、读副本、fanout)
用什么数指导什么决策:
现实案例:Twitter 公开过其 timeline 是「读 ~30 万 QPS、写 ~数千 QPS」的极端读多写少,正是这个比例催生了 fanout-on-write 的预计算 timeline(见 Day 14)。面试里给出峰值因子并出声说明「我假设晚高峰是均值的 2 倍」,比直接报一个数更得分。

3. 存储、带宽、内存:三件套一次算清

核心 trade-off:把『元数据』和『大对象/媒体』分开估,否则一个数量级的错算会让整张表失真。 三个量各有公式,关键是选对乘子

# 存储 = 写入量/天 × 对象大小 × 留存时长
元数据: 2e8/day × 1KB × 365 × 5年 ≈ 365 TB   → 分片 + 冷热分层
媒体:   假设 30% 帖子带图, 每图 200KB
        2e8 × 0.3 × 200KB ≈ 12 TB/day → 对象存储(S3)+CDN, 不进 DB

# 带宽 = 峰值 QPS × 每次响应字节
读出口: 10万 QPS × (20条 × 1KB) = 2 GB/s ≈ 16 Gbps  (仅元数据)
        媒体带宽走 CDN, 由边缘扛, 不压源站

# 缓存内存 = 热数据量 × 命中目标
热 timeline: 20% DAU = 2000万活跃 × 每人缓存200条id × ~50B
           ≈ 200 GB → 一个中等 Redis 集群放得下
反复出错的乘子:留存时长——5 年 vs 永久差几十倍;② 副本数——存储要 ×3(三副本),别报裸容量;③ 媒体没分开——把 200 KB 的图按 1 KB 算,存储和带宽全错一个数量级。
现实案例:对象存储/CDN 分流是行业共识——Instagram、YouTube、Netflix 的「大字节」全部走 blob + CDN,源站只存与传元数据和索引。估算时把这两类拆开,是和「只会背组件」的候选人拉开差距的地方。

4. 假设的纪律:显式声明、标注来源、做敏感性检查

核心 trade-off:估算的可信度不在于小数点,而在于假设是否被讲清、是否经得起『换个数会怎样』。 一个好的估算把每个输入数字显式写出来并标注来源:哪些是题目给的(DAU)、哪些是行业常识(读写比)、哪些是你拍的(峰值因子)。然后做敏感性检查——哪个假设错一倍会让结论翻盘?那个就是要和面试官确认、或在生产里重点监控的。

# 假设清单(estimation 的真正交付物)
DAU         = 1e8     [题目给定]
帖/人/天     = 2       [假设 · 类 IG 中位用户]
刷feed/人/天 = 50      [假设 · 偏高, 是敏感项]   ← 错一倍 → 读QPS翻倍
峰值因子     = 2.5     [假设 · 晚高峰经验值]
对象大小     = 1 KB    [元数据, 媒体另算]
留存        = 5 年     [产品决策, 待确认]

敏感性: 读QPS 对「刷feed次数」线性敏感 → 优先核实
        存储 对「留存」线性敏感       → 优先和产品确认
面试 vs 生产,两种不同的『算』:
现实案例:SRE 实践里,BOTE 估算用于「给压测设一个预期量级」和「容量规划初稿」,真正的扩容决策由 SLO 监控和压测数据驱动——估算负责提出假设,生产负责证伪假设。两者不是替代关系,是接力关系。

扩展与优化(估算之后做什么)

常见陷阱 + 面试问题

1. 直接报峰值忘了÷峰值,或报平均忘了×峰值。 永远讲清你报的是平均还是峰值,部署按峰值、成本算按平均。
2. 漏了副本与开销系数。 裸数据 ×3 副本、×(索引+WAL 开销)、再留 headroom。报「365 TB」时说清是逻辑量还是物理占用。
3. 把媒体和元数据混在一起算。 200 KB 的图和 1 KB 的文本差 200 倍,必须分开,且媒体走 CDN 不压源站。
4. 追求精确,纠结 86400 还是 10^5。 估算要的是数量级,10^5 足够;把时间花在「哪个假设最敏感」上。
5. 不声明假设,直接报数。 面试官无法判断你对错,也无法和你讨论——估算的交付物是假设链,不是一个孤零零的数字。

高频追问:① 一天大概多少秒,为什么能近似成 10^5?② 这个系统单机扛得住吗,你怎么一眼判断?③ 如果 DAU 涨 10 倍,哪个量先撑爆、架构要怎么变?④ 你这个峰值因子哪来的,错一倍结论会怎样?⑤ 存储要不要算副本和留存,怎么估冷热分层?

深入资源

深入思考(点击展开答案)

1. 为什么「一天 ≈ 10^5 秒」这个近似在估算里几乎从不出问题?什么时候它会咬你?

真实是 86400 ≈ 8.64×10^4,用 10^5 高估了约 16%。这在估算里恰好是良性的:用 10^5 当分母会让平均 QPS 偏低约 16%,但我们紧接着乘 2~3 的峰值因子,这点偏差被峰值因子的不确定性完全淹没——你对峰值因子的把握远没到 16% 的精度。所以数量级层面零影响。

它咬你的场景:当流量不是按天均摊,而是高度集中——比如一个每天只在 8 点开抢 5 分钟的秒杀。这时「÷10^5」算出的平均 QPS 毫无意义,真实瞬时 QPS = 当天总量 ÷ 300 秒,可能是「平均」的几百倍。教训:均摊到秒只在流量大致均匀时成立,集中型负载必须按实际活跃窗口做分母。

2. 同样是 1 亿 DAU,为什么写 QPS 才几千、不用分片,读 QPS 却到十万、必须分片?这个不对称从哪来?

根源是读写比放大效应叠加。其一,人均读次数(刷 50 次)天然就比写次数(发 2 条)高一个数量级。其二,一次「读 feed」在后端往往是一次请求 → 拉 N 条帖子 + 计数 + 关注关系的扇出,读放大让后端实际读操作更多。其三,写有天然上限(人一天发不了几百条),读却可以无聊地刷——产品形态决定读几乎不封顶。

所以社交/内容系统几乎全是「写库轻松、读路径吃紧」。这正解释了为什么这类系统的核心武器是缓存、读副本、预计算 timeline(fanout-on-write),而不是写分片。反例是 IoT/日志摄入——写远大于读,架构重心就完全反过来,落到 Kafka + LSM 分片写。一句话:读写比决定你往哪边堆机器。

3. 你估出元数据 5 年要 365 TB。面试官问「那要几台机器」,怎么从存储量推到机器数而不踩坑?

不能直接拿 365 TB ÷ 单盘容量。要先把逻辑量放大成物理占用:① ×3 副本(高可用)→ ~1.1 PB;② 加索引、WAL/redo、碎片与预留空间,工程上常再 ×1.3~2;③ 磁盘不能填满,留 30% headroom。这样物理需求可能逼近 2 PB 量级。

再除以单机有效容量,并且瓶颈未必是容量:单台数据库节点的限制往往先到 IOPS/连接数/内存,而不是磁盘空间——一台塞 10 TB 数据但扛不住 QPS 是常态。所以正确答案是「按容量和 QPS 两个约束取较紧的那个定节点数,再 ×副本」。只报「365 TB ÷ 16 TB ≈ 23 台」是典型失分点,因为它同时漏了副本、开销和 QPS 约束。

4. 估算只能到「正确的 10 倍以内」。既然这么糙,它凭什么能指导价值千万的架构决策?

因为架构选项之间往往差不止一个数量级,而决策只需要分辨数量级。「单机能扛 vs 要一千台」「写主库够用 vs 必须分布式存储」「数据进内存 vs 必须落盘分层」——这些分叉点之间是 10×、100× 的鸿沟,估算的 ±3 倍误差根本跨不过去,所以足够把你推到正确的那一侧。

反过来,估算不能回答的是数量级内部的精调:要 8 台还是 12 台、缓存设 50 GB 还是 80 GB——这些差异在估算精度内不可分辨,必须交给压测和生产指标。所以正确用法是:用估算选架构形态(粗决策),用压测和监控定具体容量(细决策)。把估算当精确预算用,或反过来嫌它不精确而不用,都是误用。

5. 如果面试官把约束从「1 亿 DAU 社交」换成「1 万企业租户、每租户 500 人的 B2B SaaS」,你的估算方法要怎么改?

总用户数 1 万 × 500 = 500 万,比 C 端小两个数量级,但方法的重心要变。其一,B2B 的「人均行为」完全不同——工作时间内高频、夜间几乎为零,峰值因子更极端(工作日 9-18 点集中),但跨时区可削峰,要按租户时区分布修正。其二,关键约束从「总量」变成「单租户上限租户间隔离」:最大租户可能比中位数大 100 倍(power-law),按平均估算会让大租户压垮共享资源,所以要估「最大租户」而非「平均租户」。其三,多租户存储要算每租户开销(独立 schema/索引的固定成本 × 1 万),小租户的固定开销总和可能超过数据本身。一句话:C 端估算看总量与峰值,B2B 估算看租户分布的尾部与隔离成本(呼应 Day 27 多租户与成本工程)。