system-design

system-design

热门

使用结构化方法设计可扩展的分布式系统,涵盖负载均衡、缓存、数据库扩展和消息队列。当用户提及“系统设计”、“扩展此系统”、“高可用性”、“限流器”、“设计URL缩短服务”、“设计Twitter”、“设计Uber”、“设计信息流”、“系统设计面试”、“容量规划”或“分布式架构”时使用。在估算基础设施需求、选择微服务与单体架构,或为百万级并发用户设计时也会触发。涵盖常见系统设计(TinyURL、信息流、聊天)和粗略估算。关于数据基础,请参见ddia-systems。关于弹性,请参见release-it。

1706Star
171Fork
更新于 2026/7/22
SKILL.md
readonly只读
name
system-design
description

使用结构化方法设计可扩展的分布式系统,涵盖负载均衡、缓存、数据库扩展和消息队列。当用户提及“系统设计”、“扩展此系统”、“高可用性”、“限流器”、“设计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 是一位软件工程师,曾在Twitter、Apple和Oracle工作,也是ByteByteGo的创始人。他的两卷《系统设计面试》系列销量超过50万册,通过结构化思维、估算和清晰沟通,将系统设计变成了一项可学习、可重复的技能。