
系统设计(一):以系统思维思考——负载、延迟与估算的艺术
掌握系统设计的基础能力:快速估算(back-of-envelope estimation)、可用性数学推导、SLA 定义,以及一套可复用的系统设计框架。
一位朋友曾请我帮忙排查一个性能问题。他们的照片分享应用在开发环境运行良好,但一到生产环境就彻底崩溃:数据库过载、API 网关频繁超时,用户大量看到 504 错误。当我问系统每秒处理多少请求时,对方回答“我不知道”;再问预期负载是多少,他说“根本没想过这个问题”。
这次对话恰恰点出了系统设计的核心意义——它远不止是在白板上画几个方框和箭头,而是构建一套心智模型,让你能在系统崩溃前就预判其行为。
系统设计究竟是什么?#
系统设计是为满足特定需求而定义系统架构、组件、模块、接口与数据流的过程。但这个教科书式的定义其实偏离了重点。实际上,系统设计是一门在不确定性中做出明智权衡的学科。

每个系统设计决策都涉及权衡:
- 更多缓存意味着更低延迟,但一致性更难保证
- 更多副本带来更高可用性,却也增加了运维复杂度
- 更多微服务支持独立部署,但也引入分布式系统的典型难题
- 更强的一致性保障会带来更高延迟和更低吞吐量
- 更多数据反规范化能加速读取,但会让写入逻辑变得更复杂
目标从来不是追求完美,而是在真正落地前,就清晰理解每个选择可能带来的后果。
系统设计远不止于面试#
人们常把系统设计当作一种面试技巧,但这大大低估了它的价值。同样的思维方式,其实每天都在真实工程场景中发挥作用:
- 为新服务在 SQL 和 NoSQL 数据库之间做选型
- 决定是加一层缓存,还是直接扩容数据库
- 评估系统能否扛住营销活动带来的 10 倍流量激增
- 设计从单体架构迁移到微服务的可行路径
- 排查由级联故障引发的线上事故
初级工程师和高级工程师的差距,往往不在于编码能力,而在于对大规模系统的推理能力。初级工程师实现一个功能;高级工程师则会追问:“当 10,000 个用户同时触发这个功能时会发生什么?当下游服务变慢时怎么办?当数据库磁盘写满时系统如何表现?”
核心原则#
在深入估算技巧之前,先明确支撑所有优秀系统设计的三大基石:

可扩展性(Scalability):系统能够平滑应对增长——更多用户、更多数据、更多请求——而无需彻底重构。可扩展性分为垂直扩展(升级单机配置)和水平扩展(增加机器数量)。实践中几乎总是优先选择水平扩展,因为它没有理论上限,还能天然提供冗余。
可靠性(Reliability):即使出现硬件故障、软件缺陷、人为误操作或流量突增,系统仍能持续正确运行。可靠性通过冗余设计、故障隔离和优雅降级来实现。
可维护性(Maintainability):系统能被团队长期理解、修改和运维。这意味着清晰的抽象边界、完善的监控体系、简单的部署流程和准确的文档。一个运行完美却无法在出问题时快速定位和修复的系统,绝不能算作好设计。
这三者常常相互冲突:高度可扩展的系统可能更难维护(组件太多);高度可靠的系统可能牺牲部分性能(例如共识协议会引入额外延迟)。系统设计的艺术,就在于为你的具体场景找到恰当的平衡点。
快速估算(Back-of-Envelope Estimation)#

系统设计中最宝贵的一项技能,就是能快速、粗略地估算关键指标的数量级。你不需要精确数字,只需要数量级上的正确性。误差在 2 倍以内完全可以接受;但如果差了 100 倍,那说明你的架构方向已经错了。

2 的幂次(Powers of 2)#
务必牢记这些数值,它们在估算存储、内存和网络容量时会反复出现。

| 幂次 | 精确值 | 近似值 |
|---|---|---|
| 2^10 | 1,024 | 1 千(1 KB) |
| 2^20 | 1,048,576 | 1 百万(1 MB) |
| 2^30 | 1,073,741,824 | 1 十亿(1 GB) |
| 2^40 | ~1.1 万亿 | 1 万亿(1 TB) |
| 2^50 | ~1.1 千万亿 | 1 拍字节(1 PB) |
每位程序员都应熟记的延迟数字#
这些数值最初由 Jeff Dean 整理,并随硬件演进不断更新,构成了性能推理的基石。具体数值虽会随代际变化,但相对量级却异常稳定。
| 操作 | 延迟 | 备注 |
|---|---|---|
| L1 缓存访问 | 0.5 ns | 片上缓存,近乎免费 |
| L2 缓存访问 | 7 ns | 仍在芯片上,约为 L1 的 14 倍 |
| 主内存访问 | 100 ns | 芯片外但本地,速度尚可 |
| SSD 随机读 | 16 μs | 约为主存的 150 倍 |
| HDD 随机读 | 2 ms | 约为主存的 20,000 倍 |
| 1 KB 数据经 1 Gbps 网络传输 | 10 μs | 小载荷下网络极快 |
| 1 MB 数据顺序读内存 | 250 μs | 内存带宽很高 |
| 1 MB 数据顺序读 SSD | 1 ms | SSD 顺序读表现优异 |
| 1 MB 数据顺序读 HDD | 20 ms | HDD 顺序读尚可接受 |
| 同数据中心往返 | 500 μs | 网络跳转成本很低 |
| 跨洲往返 | 150 ms | 光速成为瓶颈 |
这张表的关键启示:
- 内存随机访问比磁盘快 100–1000 倍
- 所有存储介质上,顺序访问都远快于随机访问
- 数据中心内部的网络往返延迟极低(约 0.5 ms)
- 跨地域网络调用代价高昂(约 150 ms)
- 将热点数据缓存在内存中,能有效规避最昂贵的操作
时间单位换算#
估算时随时调用:
| |
存储估算#
以下是估算存储需求的标准流程。

步骤 1:确定日活跃用户数(DAU)
从产品假设出发。例如,一款新的照片分享 App 可假设 DAU 为 1000 万。
步骤 2:估算每位用户每日操作数
每位用户平均每天上传 2 张照片,浏览 50 张照片。
步骤 3:计算每秒请求数(QPS)
| |
步骤 4:估算单条数据的存储大小
一张照片通常以多种尺寸存储:
- 原图:2 MB
- 中等尺寸:200 KB
- 缩略图:20 KB
- 元数据(JSON):1 KB
- 每张照片总计:约 2.2 MB
步骤 5:计算存储增长量
| |
步骤 6:规划数据保留与副本策略
如果照片永久保存,并为持久性做 3 副本复制:
- 第一年:16 PB × 3 = 48 PB 原始存储
至此你已明确:必须使用分布式对象存储系统,而不是单台数据库服务器。仅凭这一估算,就避免了一个根本性的架构错误。
带宽估算#
带宽估算直接源于你的 QPS 和载荷大小。
| |
这些数字告诉你:
- 你必须使用 CDN。单台源站服务器根本无法承载 8 Gbps 的图片流量。
- 写入入口需要高吞吐的分布式存储支持。
- 峰值流量(通常是平均值的 2–5 倍)可能将读取带宽推高至 20–40 Gbps。
网络容量速查表#
估算带宽时,了解常见基础设施的典型吞吐能力很有帮助:
| 组件 | 典型吞吐量 |
|---|---|
| 单网卡(1 GbE) | ~100 MB/秒 |
| 单网卡(10 GbE) | ~1 GB/秒 |
| 单网卡(25 GbE) | ~2.5 GB/秒 |
| 单 SSD(NVMe) | ~3 GB/秒(顺序读) |
| 单 HDD | ~200 MB/秒(顺序读) |
| Redis 单实例 | ~100K ops/秒,~1 GB/秒 |
| PostgreSQL 单实例 | ~5K–20K QPS(取决于查询复杂度) |
| Kafka 单 Broker | ~100 MB/秒/分区 |
一旦估算出的带宽超出单个组件的承载能力,就必须将负载分散到多个实例上。这正是估算的核心价值:它在你动手构建之前,就明确告诉你哪些地方需要水平扩展。
内存估算#
内存估算决定了你能将多少数据保留在 RAM 中,直接影响缓存策略和成本。
80/20 法则(帕累托原理)#
在大多数系统中,20% 的数据贡献了 80% 的请求。将这部分“热数据”缓存在内存中,就能满足 80% 的流量,而无需访问数据库。
| |
以上数字仅为示意,但足以说明:缓存是系统设计中最具成本效益的优化手段之一。
实用内存限制参考#
| 机器类型 | 内存 | 典型用途 |
|---|---|---|
| 标准云实例(r6g.xlarge) | 32 GB | 小型缓存、单服务部署 |
| 大型云实例(r6g.4xlarge) | 128 GB | 中型缓存、Redis 节点 |
| 超大型(r6g.16xlarge) | 512 GB | 大型内存数据库 |
| Redis 集群(10 节点) | 320 GB – 5 TB | 分布式缓存 |
| 内存数据库(SAP HANA) | 最高 24 TB | 企业级分析 |
当你的缓存内存需求超出单机承载能力时,就需要引入分布式缓存(如 Redis Cluster 或 Memcached)。如果连集群都难以合理管理,那就得重新思考缓存策略——比如只缓存元数据,而非完整对象。
SLA、SLO 与 SLI#
这三个术语经常被混淆,但它们有明确区别。
SLI(Service Level Indicator,服务等级指标):对服务某一方面的量化度量。例如:
- 请求延迟(p50、p95、p99)
- 错误率(5xx 响应占比)
- 吞吐量(每秒请求数)
- 可用性(服务正常运行时间占比)
SLO(Service Level Objective,服务等级目标):为某个 SLI 设定的目标值或范围。例如:
- p99 延迟 < 200ms
- 错误率 < 0.1%
- 可用性 > 99.9%
SLA(Service Level Agreement,服务等级协议):服务提供方与客户之间的合同,明确规定 SLO 及未达标时的补救措施。例如:
- “若月度可用性低于 99.9%,客户可获得 10% 的服务抵扣”
- “若 p99 延迟连续超过 500ms 达 1 小时,则触发事故响应流程”
三者关系层层递进:SLI 是测量值,SLO 是 SLI 的目标,SLA 则将 SLO 正式化为具有约束力的契约。
可用性数学#
可用性以给定周期内正常运行时间的百分比表示,业界习惯用“nines”(几个 9)来简称。
| 可用性 | 年停机时间 | 月停机时间 | 周停机时间 |
|---|---|---|---|
| 99%(两个 9) | 3.65 天 | 7.31 小时 | 1.68 小时 |
| 99.9%(三个 9) | 8.77 小时 | 43.83 分钟 | 10.08 分钟 |
| 99.95% | 4.38 小时 | 21.92 分钟 | 5.04 分钟 |
| 99.99%(四个 9) | 52.60 分钟 | 4.38 分钟 | 1.01 分钟 |
| 99.999%(五个 9) | 5.26 分钟 | 26.30 秒 | 6.05 秒 |
串行 vs 并行可用性#
当组件串联(所有组件必须正常,系统才可用)时:
| |
即使每个组件都达到三个 9 的可用性,整体系统仍不足三个 9。这解释了为何链路中的每个环节都至关重要。
当组件并联(任一组件正常,系统即可用)时:
| |
冗余能极大提升整体可用性。两个三个 9 的组件并联,组合后可达六个 9。这正是复制(replication)成为可靠系统基石的原因。
“Nines”的成本#
每增加一个 9,成本大约增加 10 倍。从 99.9% 提升到 99.99%,并非只是提升 0.09%;而是将年允许停机时间从 8.77 小时压缩到 52.6 分钟。这要求:
- 自动故障转移(无需人工介入)
- 多地域部署(抵御整个数据中心的故障)
- 全面的监控与告警体系
- 自动回滚机制(应对错误发布)
- 混沌工程(主动暴露潜在故障模式)
大多数应用应瞄准 99.9% 至 99.95% 的可用性。四个 9 及以上,通常只用于支付系统、核心数据库等关键基础设施。
容量规划(Capacity Planning)#

容量规划是确定生产环境所需资源以应对动态需求的过程。以下是几个关键概念。
峰值 vs 平均值#
生产系统必须能应对峰值流量,而非平均流量。不同应用类型的峰值/平均比差异很大:
| 应用类型 | 峰值/平均比 |
|---|---|
| 电商(常规) | 2–3x |
| 电商(黑色星期五) | 5–10x |
| 社交媒体 | 2–4x |
| 企业级 SaaS | 1.5–2x |
| 游戏 | 3–5x |
预留余量(Headroom)#
切勿让系统满负荷运行。行业标准是预留 30–50% 的余量:
| |
突发流量应对#
即使预留了余量,突发流量仍可能超出规划容量。常用策略包括:
- 自动扩缩容(Auto-scaling):基于负载指标动态增减实例(适用于无状态服务)
- 限流(Rate limiting):优雅拒绝超额请求(返回 429 而非直接崩溃)
- 队列式负载均衡(Queue-based load leveling):将请求暂存队列,按可持续速率处理
- 熔断器(Circuit breakers):当下游服务过载时快速失败,防止雪崩效应
系统设计框架#
无论是在面试还是真实架构讨论中,结构化方法都能帮你避免迷失方向。以下是一套可复用的框架。
步骤 1:明确需求(5 分钟)#
区分功能性需求(系统做什么)和非功能性需求(做得有多好)。
功能性:
- 核心功能有哪些?
- 用户是谁?
- 输入与输出是什么?
非功能性:
- 预期规模(用户数、QPS、数据量)?
- 延迟要求?
- 所需可用性等级?
- 可接受的一致性模型?
步骤 2:快速估算(5 分钟)#
计算:
- QPS(读/写分开)
- 存储需求(日/月/年)
- 带宽需求
- 内存需求(用于缓存)
步骤 3:高层设计(10 分钟)#
勾勒主要组件:
- 客户端层
- API 层(端点、协议)
- 应用层(业务逻辑)
- 数据层(数据库、缓存、对象存储)
- 支撑设施(消息队列、搜索、CDN)
并端到端描述主用例的数据流。
步骤 4:深度剖析(15 分钟)#
选取 2–3 个关键组件深入探讨:
- 数据库 Schema 设计
- 缓存策略
- 数据分片(Partitioning)
- 复制与一致性
- API 接口设计细节
步骤 5:识别瓶颈与改进点(5 分钟)#
- 单点故障及其消除方案
- 性能瓶颈及优化路径
- 监控与告警策略
- 未来扩展考量
真实案例:照片分享 App 的需求估算#
我们以类似 Instagram 的照片分享应用为例,走一遍完整的估算流程。
需求#
功能性:
- 上传照片
- 查看关注用户的动态流(feed)
- 关注/取消关注用户
- 点赞与评论照片
非功能性:
- 总用户数 5 亿,日活用户(DAU)1 亿
- 用户平均每天上传 1 张照片
- 用户平均每天查看动态流 10 次,每次看到约 20 张照片
- 照片加载时间 ≤ 200ms
- 可用性:99.9%
- 动态流更新可接受最终一致性
估算#
写 QPS(上传):
| |
读 QPS(动态流浏览):
| |
这是一个典型的读多写少系统,读写比约为 200:1。
存储:
| |
带宽:
| |
920 Gbps 的读取带宽极为庞大。这确认我们必须:
- 使用多地域 CDN,在边缘节点就近服务图片
- 对热门照片进行激进缓存
- 构建多层缓存(CDN、应用层、数据库层)
缓存内存: 依据 80/20 法则(20% 的照片产生 80% 的流量),我们缓存热数据集:
| |
200 GB 缓存可在 Redis 集群中轻松实现(例如 10 台机器,每台 32 GB 内存,预留 Redis 开销余量)。
架构概要#
基于上述估算,系统需包含:
- 对象存储(S3/GCS)存放照片文件 —— 任何关系型数据库都无法承载 91 PB/年的增长
- CDN 支撑读取路径 —— 920 Gbps 流量无法由源站服务器直接提供
- 分布式缓存(Redis 集群)缓存照片元数据 —— 200 GB 热数据集
- 关系型数据库 存储用户数据、关注关系、点赞记录 —— 数据量相对较小
- 消息队列 处理异步任务 —— 动态流生成、通知推送、图片处理
- 搜索/索引服务 支撑发现功能
估算驱动我们走向了正确的架构,全程未画一张图。这正是快速估算(back-of-envelope math)的力量。
常见估算错误#
忽略峰值:平均 QPS 对容量规划毫无意义。务必乘以 2–5 倍作为峰值系数。
忽视读写比:多数系统读远多于写(100:1 或更高)。该比率直接决定你的缓存策略、复制拓扑与数据库选型。
假设线性增长:用户基数与数据量极少线性增长。应为未来 3–5 年的指数级增长做好准备。
混淆存储与带宽:100 TB 的静态存储 ≠ 100 TB 的带宽需求。带宽取决于访问模式,而非总数据量。
忘记副本开销:若为持久性做 3 副本,实际存储成本是你估算值的 3 倍。
追求精确数字而非数量级:估算的目的,是判断你需要 1 台服务器还是 100 台,而非 47 台还是 53 台。大胆四舍五入,善用 10 的幂次。
估算速查表#
以下是常见系统设计估算的快速参考。将这些数字印在脑中,助你快速推理。
| |
一致性模型:预览#
在推理分布式系统时,你会遇到不同的一致性模型。此处仅作简要预览,因为这些概念将在本系列后续文章中反复出现:
强一致性(Strong consistency):一次写入完成后,所有后续读取均返回最新值。这是单数据库服务器配合 ACID 事务所能提供的保证。它易于推理,但在分布式系统中实现成本高昂。
最终一致性(Eventual consistency):一次写入完成后,读取可能暂时返回旧值,但最终所有读取都将返回最新值。这是大多数缓存层、DNS 传播及许多 NoSQL 数据库采用的模型。它以更高可用性与更低延迟为代价,换取更强的伸缩性。
因果一致性(Causal consistency):若操作 A 在因果上依赖于操作 B(例如,回复依赖于原始消息),则任何看到 A 的进程也必然看到 B。它比最终一致性更强,但弱于强一致性,是许多社交应用的理想折中点。
一致性模型的选择,是系统设计中最具决定性的决策之一,并直接关联 CAP 定理:在网络分区(network partition)发生时,分布式系统必须在一致性(Consistency)与可用性(Availability)之间做出取舍。绝大多数大规模系统对大部分操作选择可用性与最终一致性,仅在支付处理等关键路径上保留强一致性。
下一步#
掌握了估算技能后,下一篇文章将聚焦于每个 Web 请求的前三跳:DNS 解析、CDN 缓存与负载均衡。这些组件位于用户与你的应用服务器之间,它们的设计是否合理,直接决定了你的系统能否承载你刚刚估算出的巨大负载。