
system-design
热门使用结构化方法设计可扩展的分布式系统,涵盖负载均衡、缓存、数据库扩展和消息队列。当用户提及“系统设计”、“扩展此系统”、“高可用性”、“限流器”、“设计URL缩短服务”、“设计Twitter”、“设计Uber”、“设计信息流”、“系统设计面试”、“容量规划”或“分布式架构”时使用。在估算基础设施需求、选择微服务与单体架构,或为百万级并发用户设计时也会触发。涵盖常见系统设计(TinyURL、信息流、聊天)和粗略估算。关于数据基础,请参见ddia-systems。关于弹性,请参见release-it。
使用结构化方法设计可扩展的分布式系统,涵盖负载均衡、缓存、数据库扩展和消息队列。当用户提及“系统设计”、“扩展此系统”、“高可用性”、“限流器”、“设计URL缩短服务”、“设计Twitter”、“设计Uber”、“设计信息流”、“系统设计面试”、“容量规划”或“分布式架构”时使用。在估算基础设施需求、选择微服务与单体架构,或为百万级并发用户设计时也会触发。涵盖常见系统设计(TinyURL、信息流、聊天)和粗略估算。关于数据基础,请参见ddia-systems。关于弹性,请参见release-it。
系统设计框架
一种设计大规模分布式系统的结构化方法。在架构新服务、评审设计、估算容量或准备系统设计讨论时应用这些原则。
核心原则
从需求出发,而非解决方案。 在理解约束之前直接跳到架构会导致过度设计或设计不足。可扩展系统由成熟的构建模块(负载均衡器、缓存、队列、数据库、CDN)组装而成——关键在于选择合适的模块,通过估算确定规模,并承担每个选择带来的权衡。
评分
目标:10/10。 根据设计满足快速诊断表中多少行来评分——score = round(passed / 8 × 10):9-10 = 全部或几乎所有行通过——明确的需求、真实的估算、冗余、明确的数据库扩展和缓存策略、通过队列实现异步、监控以及部署计划,并指出权衡;5-6 = 设计可行但忽略了估算、冗余或运维;<=3 = 在需求或估算之前就提出了架构。始终说明当前分数,指出未通过的诊断行,并给出每个问题的具体修复方法。
系统设计框架
构建可靠、可扩展分布式系统的六个方面:
1. 四步流程
核心概念: 每个设计遵循四个阶段:(1) 理解问题并确定范围,(2) 提出高层设计并获得认可,(3) 深入关键组件,(4) 总结权衡和未来改进。
为什么有效: 没有结构,设计要么过于抽象,要么陷入过早的细节。四步流程按比例投入时间——先画轮廓,再深入关键点。
关键见解:
- 步骤1(约5-10分钟):澄清问题,功能和非功能需求,商定规模(DAU、QPS、存储)
- 步骤2(约15-20分钟):高层图,包括API、服务、数据存储、数据流箭头
- 步骤3(约15-20分钟):详细设计2-3个最难或最关键的组件
- 步骤4(约5分钟):权衡、瓶颈、未来改进
- 切勿跳过步骤1——模糊的范围会浪费所有后续工作;明确达成假设共识
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 新服务启动 | 涵盖所有四步的一页设计文档,然后编码 | 需求、API契约、数据模型、容量估算,然后实现 |
| 架构评审 | 按顺序引导评审者通过各步骤 | 范围、图、风险最高组件的深入分析、开放问题 |
| 事故复盘 | 通过四步流程追溯故障 | 遗漏了哪个需求?哪个模块失败?哪个权衡导致了问题? |
参见 references/four-step-process.md 以端到端运行设计——每阶段时间分配、示例澄清问题以及每个步骤的技巧。
2. 粗略估算
核心概念: 使用2的幂、延迟数字和简单算术来估算QPS、存储、带宽和服务器数量,然后再确定架构。
为什么有效: 估算可以防止过度配置(浪费资金)和配置不足(负载下宕机)。2分钟的计算可以节省数周的重做工作。
关键见解:
- 2的幂:2^10 ≈ 1千,2^20 ≈ 1百万,2^30 ≈ 10亿,2^40 ≈ 1万亿
- 延迟:内存读取 ~100 ns,SSD读取 ~100 us,磁盘寻道 ~10 ms,同数据中心往返 ~0.5 ms,跨洲 ~150 ms
- 可用性9s:99.9% = 每年8.77小时停机;99.99% = 每年52.6分钟
- QPS:DAU × 每日操作数 / 86,400秒;峰值通常为平均值的2-5倍
- 存储:每日记录数 × 记录大小 × 保留时间
- 大胆取整——目标是数量级,而非精确值
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 容量规划 | 估算QPS,乘以增长因子 | 1亿DAU × 5操作 / 86400 = ~5,800 QPS平均,~30K峰值 |
| 存储预算 | 每条记录大小 × 数量 × 保留时间 | 5亿条推文/天 × 300字节 × 365天 = ~55 TB/年 |
| SLA定义 | 将9s转换为允许的停机时间 | 四个9 = 每年约52分钟停机 |
参见 references/estimation-numbers.md 以确定系统规模——完整的延迟表、可用性9s表以及QPS/存储/带宽计算示例。
3. 构建模块
核心概念: 可扩展系统由标准工具包组装而成:DNS、CDN、负载均衡器、反向代理、应用服务器、缓存、消息队列和一致性哈希。
为什么有效: 每个模块用一种成本换取另一种(缓存用新鲜度换取读取速度;队列用延迟换取解耦),因此只在出现特定瓶颈时才引入模块——一开始就全部添加只会增加故障模式。
关键见解:
- 负载均衡器:L4(传输层——快速、简单) vs L7(应用层——内容感知路由)
- 缓存层:客户端、CDN、Web服务器、应用(Redis/Memcached)、数据库查询缓存
- 缓存策略:旁路缓存(应用管理)、穿透缓存、同步写入、异步写入
- 消息队列(Kafka、RabbitMQ、SQS):解耦生产者和消费者,吸收峰值,支持异步处理
- 一致性哈希:在节点变化时以最小重新分配方式将键分布到节点
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 读密集型工作负载 | 数据库前使用旁路缓存Redis | 缓存用户配置文件并设置TTL;写入时失效 |
| 流量峰值 | API和工作节点之间的消息队列 | 将图片调整大小任务入队;工作节点按自己的节奏拉取 |
| 全球用户 | 静态资源的CDN | 从边缘提供JS/CSS/图片;源站仅提供API |
| 不均匀负载 | 分片分配的一致性哈希 | 添加节点仅移动约1/n的键 |
参见 references/building-blocks.md 以选择组件——DNS、CDN、负载均衡器、缓存策略、消息队列和一致性哈希的工作原理及引入时机。
4. 数据库设计与扩展
核心概念: 根据数据形状和访问模式选择SQL vs NoSQL;先垂直扩展,达到垂直极限后再水平扩展(复制和分片)。
为什么有效: 数据库通常是第一个瓶颈。理解复制、分片和反规范化的权衡可以推迟昂贵的重新架构,并使增长更有计划。
关键见解:
- 垂直扩展更简单但有上限;水平扩展更难但几乎无限
- 复制:主从(一个写入者,多个读取者)适用于读密集型;多主适用于多区域写入
- 分片:基于哈希(均匀分布,范围查询困难)、基于范围(范围查询容易,热点风险)、基于目录(灵活,额外查找)
- SQL适用于ACID事务、连接、定义的模式;NoSQL适用于灵活模式、水平扩展、非常高的写入吞吐量
- 反规范化用存储和写入复杂度换取读取速度——当读取占主导且数据变化不频繁时使用
- 名人/热点问题:一个热点分片需要二次分区或缓存层
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 读密集型API | 带只读副本的主从 | 读取到副本,写入到主库;接受轻微延迟 |
| 大规模用户数据 | 基于user_id的哈希分片 | hash(user_id) % num_shards;均匀、独立的分片 |
| 分析仪表板 | 反规范化的物化视图 | 每晚预连接和聚合;从物化表提供 |
参见 references/database-scaling.md 以应对数据库瓶颈——复制拓扑、三种分片策略比较、反规范化权衡以及SQL vs NoSQL选择指南。
5. 常见系统设计
核心概念: 大多数系统是少量已知设计的变体:URL缩短服务、限流器、通知系统、信息流、聊天、搜索自动补全、网络爬虫、唯一ID生成器。
为什么有效: 已知设计的心理库让你能够识别新问题类似哪种模式并进行调整,而不是从头发明。
关键见解:
- URL缩短服务:base62编码、键值存储、301 vs 302重定向权衡(缓存 vs 分析)
- 限流器:网关处的令牌桶或滑动窗口;返回429并附带Retry-After
- 信息流:写入时扇出(发布时推送) vs 读取时扇出(读取时拉取);名人使用混合模式
- 聊天:WebSocket用于实时双向消息,队列用于投递保证,心跳在线服务
- 自动补全:热门查询的Trie树;预计算并缓存流行前缀
- 网络爬虫:BFS与URL边界,礼貌性(robots.txt、每域速率限制),通过内容哈希去重
- 唯一ID:UUID(简单,无需协调) vs Snowflake(64位,时间可排序,数据中心感知)
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 短链接服务 | 对自增ID或哈希进行Base62编码 | https://short.ly/a1B2c3 映射到键值行 |
| API保护 | 网关处的令牌桶 | 每个密钥每分钟100个令牌;稳定补充;拒绝时返回429 |
| 社交信息流 | 混合扇出 | 预计算粉丝数<10K的账户的信息流;读取时合并名人帖子 |
参见 references/common-designs.md 以应对类似已知设计的问题——URL缩短服务、限流器、信息流、聊天、自动补全、网络爬虫和唯一ID生成器的完整指南。
6. 可靠性与运维
核心概念: 系统的好坏取决于其保持运行、恢复和可观测的能力。健康检查、监控、日志记录和部署策略是首要的设计关注点,而非事后考虑。
为什么有效: 生产系统以图表从未预测的方式失败。运维准备——指标、告警、回滚计划、冗余——决定了故障是短暂波动还是长时间宕机。
关键见解:
- 健康检查:存活检查(进程是否存活?)和就绪检查(能否处理流量?)——Kubernetes两者都使用
- 可观测性三大支柱:指标(Prometheus、Datadog)、日志(ELK、CloudWatch)、追踪(Jaeger、Zipkin)
- 部署:滚动(逐步)、蓝绿(相同环境间即时切换)、金丝雀(先小比例)
- 灾难恢复:RPO(可接受的数据丢失)和RTO(可接受的恢复时间)驱动备份和故障转移策略
- 多数据中心:主备(故障转移)或主主(需要数据同步和冲突解决)
- 自动扩展:基于CPU、内存、队列深度或自定义指标扩展;始终设置最小和最大数量
代码应用:
| 上下文 | 模式 | 示例 |
|---|---|---|
| 零停机部署 | 带健康检查门的蓝绿部署 | 检查通过后切换到绿色;保留蓝色作为即时回滚 |
| 逐步发布 | 带指标比较的金丝雀 | 5%流量到新版本;比较错误和延迟;推广或回滚 |
| 数据安全 | 定义RPO/RTO并相应实施 | RPO 1小时 = 每小时备份;RTO 5分钟 = 自动故障转移 |
参见 references/reliability-operations.md 以加固生产环境——健康检查模式、可观测性支柱、部署策略、灾难恢复(RPO/RTO)和自动扩展。
常见错误
| 错误 | 失败原因 | 修复方法 |
|---|---|---|
| 先架构后需求 | 解决错误问题,遗漏约束 | 花前5-10分钟确定范围:功能、规模、SLA |
| 没有估算 | 配置偏差数量级 | 在选择组件前估算QPS、存储、带宽 |
| 单点故障 | 一个组件导致整个系统宕机 | 每层冗余:多服务器、多可用区、多区域 |
| 过早分片 | 巨大的运维复杂性,远早于需要 | 先垂直扩展,然后只读副本,积极缓存,最后分片 |
| 缓存无失效 | 数据陈旧导致错误和混乱 | 定义TTL;旁路缓存并在写入时显式失效 |
| 处处同步调用 | 一个慢服务将延迟级联到所有调用者 | 非延迟关键路径使用队列;同步调用设置超时 |
| 忽略热点 | 一个分片或键被高负载,其他空闲 | 检测热键;添加二次分区或本地缓存 |
| 无监控或告警 | 用户比你更早发现故障 | 从第一天起就收集指标、日志和追踪 |
快速诊断
| 问题 | 如果否 | 行动 |
|---|---|---|
| 是否列出了功能和非功能需求? | 设计基于假设 | 写下功能、DAU、QPS、存储、延迟和可用性SLA |
| 是否有QPS和存储估算? | 容量是猜测 | DAU × 操作 / 86400 计算QPS;记录数 × 大小 × 保留时间计算存储 |
| 每个组件是否冗余? | 存在单点故障 | 为每个组件添加副本、故障转移或多可用区 |
| 是否定义了数据库扩展策略? | 增长时会遇到瓶颈 | 先垂直扩展,然后只读副本,最后用清晰的分片键分片 |
| 读密集型路径是否有缓存? | 数据库承受不必要的负载 | Redis/Memcached旁路缓存,定义TTL |
| 异步路径是否使用队列? | 紧耦合,级联故障 | 使用Kafka/SQS解耦任务、通知、分析 |
| 是否有监控和告警计划? | 对生产故障视而不见 | 定义指标、日志聚合、追踪、告警阈值 |
| 是否定义了部署策略? | 一次性发布风险高 | 滚动、蓝绿或金丝雀,带自动回滚 |
延伸阅读
包含详细图表和指南的完整资料:
- 《系统设计面试——内部指南》 作者:Alex Xu(第一卷)
- 《系统设计面试——内部指南:第二卷》 作者:Alex Xu(第二卷)
- 《设计数据密集型应用》 作者:Martin Kleppmann(数据系统基础)
- ByteByteGo —— Alex Xu的平台,提供可视化系统设计解释
关于作者
Alex Xu 是一位软件工程师,曾在Twitter、Apple和Oracle工作,也是ByteByteGo的创始人。他的两卷《系统设计面试》系列销量超过50万册,通过结构化思维、估算和清晰沟通,将系统设计变成了一项可学习、可重复的技能。





