System Design
该技能用于设计或演进系统架构:从需求出发,得到可扩展、容错的设计。其核心主张是:设计中的每个组件都因某项需求而存在,且每个组件都有成本。方法按步骤顺序执行,每一步有完成标准,下一步基于上一步产出;参考资料为模式目录,当设计走到某分支时再打开对应文档。
步骤
1. 需求
功能需求:2–4 个用户核心动作(主体 + 动词 + 名词,如“用户上传文件”),并明确不在范围内的事项。
非功能需求,每项都带数字:日活与峰值并发用户、读写比、数据量与保留期、延迟目标(p99)、可用性、文件或负载大小限制,以及问题特有的约束(公平性、反机器人、断点续传、顺序性)。
按数据类型确定一致性:金额、库存、唯一性需要强一致;点赞、信息流、目录可容忍陈旧。按模块决定,有时按查询决定。
用户未给数字时要询问;无法给出时,写明假设并标注为假设。
完成标准:每项非功能需求都有数字或标注的假设,每种数据类型都有一致性立场。
2. 容量估算
只计算会驱动决策的数字,并在每个数字旁写下它驱动的决策。
| 量 | 公式 |
|---|---|
| 平均速率 | 每日数量 ÷ 86,400(约 10⁵) |
| 峰值速率 | 平均值 × 峰值系数(需说明,通常 3–10 倍,事件峰值更高) |
| 读 | 写 × 读写比 |
| 行数 | 每日写入 × 365 × 保留年数 |
| 存储 | 行数 × 平均大小 × 副本因子(× 衍生版本数) |
| 带宽 | 请求/秒 × 负载大小(媒体场景出网成本通常占主导) |
| 连接数 | 推送功能的峰值并发用户 ÷ 每节点连接数 = 节点数 |
| 标识符大小 | 满足字母表ᵏ ≥ 历史总标识符数的最小 k |
示例:35 写/秒 → 单个关系型主库足够;十年 3650 亿行 → 分区存储;每天 150 TB 上传 → 字节必须绕过应用服务器。若某数字不会改变任何决策,跳过该步并说明原因。
完成标准:每个计算出的数字都注明了它驱动的决策。
3. 实体、API、数据库
从功能需求的名词推导实体。将二进制内容与元数据分离:字节存对象存储;所有者、大小、类型、状态、对象键存数据库。
每个功能需求对应一个端点。身份取自认证令牌,绝不来自请求体。标出影响架构的协议细节(301 与 302、负载限制、长连接响应)。
根据第 2 步的数字和第 1 步的一致性立场选择数据库 → 参考 DATA.md。
完成标准:每个功能需求都有端点,每个实体都有归属存储及理由。
4. 朴素设计,然后攻破它
画出端到端满足功能需求的最简设计。然后针对每项非功能需求用最坏情况攻击它:最大文件、峰值突增、最热键、操作中途节点宕机、两次写之间的网络分区。直觉式设计会在此暴露问题,找出问题正是这一步的目的。
完成标准:得到具体失败清单,每条都对应它所违反的需求。
5. 在阶梯上逐级修复
用能解决问题的最低一级修复每个失败,并在组件旁写下它带来的权衡。阶梯从低成本到高成本:
- 更好的查询、索引或数据结构;为访问模式选对工具(搜索用搜索引擎)。
- 缓存、CDN 或预计算读模型。
- 纵向扩展。
- 无状态层置于负载均衡器之后做横向扩展。
- 只读副本。
- 异步工作:队列、worker、事件。
- CQRS、分区(分片)。
- 服务拆分、多区域。
更高一级需因必要性获得位置:说明低级别未解决的症状。阶梯之外还有两种做法:当需求远超可服务的上限(如固定票量)时,限制需求而非扩容;商品化能力(等候室、转码、托管搜索)应购买而非自建。
按失败类型可参考:SCALING.md(服务器过载、单服务器、峰值、请求中执行慢任务、全局延迟);DATA.md(数据库过载、慢查询、搜索、数据过大、ID、大文件、重复预订);DISTRIBUTED.md(双系统写入、衍生存储漂移、同步调用链、顺序、重复);STYLES.md(团队或代码边界、单体与微服务);REALTIME.md(客户端无需刷新即可获得服务端更新)。完整方法应用于已知问题的示例见 CASE-STUDIES.md。
完成标准:第 4 步的每个失败都有修复方案且写下权衡。
6. 排查单点故障
逐个组件追问:它死亡、变慢或被分区隔离时会发生什么——负载均衡器本身、数据库主库、缓存、ID 生成器、消息代理、区域。用冗余加故障转移回答,并说明数据丢失窗口(异步复制在故障转移时可能丢失最后写入)。缓存失效时应降级到数据库,并防止缓存击穿(thundering herd)。
完成标准:每个组件都有故障应对答案。
7. 对照需求验证
建立表格:需求 → 组件 → 接受的权衡。然后端到端检查:每个衍生存储(缓存、搜索索引、读模型)都有同步路径并说明陈旧程度;每条至少一次投递路径都以幂等消费者收尾;每个组件都能追溯到某项需求。
完成标准:每项需求已映射、每个组件有理由、没有任何一致性 claim 依赖未验证的路径。
原则
- 权衡:每个选择都付出代价:延迟、一致性、成本、运维负担、团队复杂度。没有写明权衡的设计未经审视。
- 事实来源:每个事实只有一个权威存储。缓存、搜索索引、读模型都是衍生的:可丢弃、可重建,由 CDC 或 outbox 喂养,而非双写。
- 必要性:微服务、事件驱动架构、CQRS、分片、WebSocket 在出现具名症状时才进入,并限定在出现症状的那部分系统。数字允许时,单服务器加单数据库就是正确设计。
- 热路径:把重活移出请求:大字节直传对象存储,慢任务进队列,昂贵聚合预计算。
- 原理优先于品牌:用工具提供的属性(分区写入、倒排索引、持久日志)来论证选型;具备该属性的任何工具都合适。
面试模式
当设计用于面试时:在第 1 步索要数字,第 1–3 步约占总时间的前四分之一,大部分时间用于问题真正考察的深入部分(大文件上传、ID 生成、顺序、竞争)。做出每个选择时都说出其权衡。
限制
资料未提供安装方式、收费模式、价格、支持平台或具体使用步骤;以上内容暂未提供。