@system_monarch: https://x.com/system_monarch/status/2057714149451497544
摘要
一份全面的系统设计模式速查表,涵盖12种模式,适用于技术面试,包括每种模式的信号、构建块和深入探讨,基于对顶尖科技公司200多次面试的经验总结。
查看缓存全文
缓存时间: 2026/05/22 13:56
系统设计模式速查手册——我真希望早点知道它
在过去 12 年里,我在 微软、Atlassian、谷歌、优步、Salesforce、亚马逊、沃尔玛 以及许多其他公司参与了 200 多场面试。
有一个习惯一次又一次地救我于水火。在画任何方框之前,我先试着给模式打上标签。
一旦模式清晰了,架构就更简单了。你的权衡也就更精准了。
你的回答听起来也不再只是一堆随机的流行语。
大多数候选人在系统设计面试中失败,不是因为他们不懂技术,而是因为他们没有先理解自己究竟要解决哪种问题,就一头扎进解决方案。信息流和支付账本完全处在不同的世界里。对前者有效的工具,对后者可能就是无声的灾难。
以下就是我希望能早点做出来的系统设计模式速查手册。每种模式包含三个子部分,分别是:「信号」、「构建模块(通常用于解决该模式)」和「深入探讨(针对该模式)」。
第 0 步:在三十秒内对问题分类
当你听到问题并记下功能需求后,在说出一个字之前,先在脑子里做这件事:
- 是读更高、写更高,还是两者同时很高?
- 用户需要立即看到更新,还是准实时就够用了?
- 主要价值在于服务用户流量,还是稍后处理数据?
- 我们是处理单个用户,还是大规模扇出到数百万人?
- 正确性是绝对的吗(金钱、库存),还是尽力而为(点赞、推荐)?
一旦回答了这些问题,你通常就能将问题映射到下面 12 种模式之一。模式会告诉你哪些原语重要,哪些权衡需要提出,以及哪些流行语是噪音。
我们开始吧。
模式 1:读密集型系统
属于此类的例子: 信息流、个人资料服务、商品目录、短链接服务的读取、内容浏览、「设计 Twitter 时间线」、「设计 Wikipedia」、「设计 CDN」。
要留意的信号
- 读写比例极度倾斜。比如 100:1、1000:1 甚至更严重。
- 对于某个用户来说,数据不是每秒钟都在变化。个人资料每周更新一次,但被人查看数千次。
- 延迟非常重要。一致性可以稍微放松——用户能容忍看到两秒前的点赞数。
- 同一份数据被很多用户请求(热门内容),或者被同一个用户反复请求(自己的信息流)。
核心构建模块
- 缓存层: Redis 或 Memcached,用于热门键、聚合数据和计算视图。多层缓存很常见——浏览器缓存、CDN、边缘缓存、应用缓存、数据库缓存。
- 只读副本(Read replicas): 额外的数据库副本来扩展读取。写入到主库,读取分散到副本。
- 搜索和辅助索引: Elasticsearch 或类似的工具,用于灵活的过滤和全文查询。
- 后台写入器 / 预计算工作者: 预先计算信息流、排行榜或聚合数据的服务,这样读取路径就只是一个查询。
- CDN: 对于任何静态或半静态内容,尽量将其推送到离用户最近的位置。
区分高级别回答的深入探讨和权衡
- 缓存策略不是「用 Redis」那么简单。 真正的讨论是你缓存什么:单个对象、完整页面、计算好的信息流,还是查询结果。每种都有不同的失效方式和不同的内存成本。
- 缓存失效才是真正的难点。 基于 TTL 的过期简单但会带来数据过时。写透缓存一致但成本高。写后缓存快但崩溃时会丢数据。旁路缓存是默认方案,原因是有经典的「惊群效应」——当热点键过期,数千个请求同时涌向数据库。可以讨论请求合并或概率性提前过期。
- 副本延迟时的一致性 vs 可用性。 用户刚发布内容后,如果读取到了延迟的副本,他们会看到什么?「读自己写」一致性通常意味着在短时间内将用户自己的读取路由到主库。
- 热点键问题。 某个名人的个人资料或一条热门推文可能让单个 Redis 分片崩溃。解决方案:将热点键复制到多个节点,在 Redis 前面使用本地进程内缓存,或者预先计算并通过静态路径提供服务。
- 写入时扇出 vs 读取时扇出。 这是 Twitter 风格信息流的经典问题。写入时扇出对普通用户很好,但对拥有 1 亿粉丝的名人则是灾难。大多数真实系统采用混合方案:对普通用户写入时扇出,对名人读取时扇出,在查询时合并。
高级候选人会说「我们会缓存它」。 Staff 级别的候选人会解释他们缓存什么、如何失效、发生惊群时怎么办、以及如何处理热点键问题。
模式 2:写密集型或事件驱动系统
属于此类的例子: 日志平台、指标采集、订单系统、点击流采集、支付事件、IoT 遥测、「设计 Datadog 这样的指标系统」、「设计分析采集管道」。
要留意的信号
- 每秒写入量巨大——通常是每分钟数百万。
- 大部分处理在用户操作完成后异步进行。用户点击,事件被捕获,实际工作稍后完成。
- 读取通常是聚合、仪表板或下游消费者,而不是单行查询。
- 丢失单个事件可能可以接受,也可能完全不可接受,具体取决于它是跟踪像素还是支付。
核心构建模块
- 仅追加日志: Kafka、Pulsar、Kinesis。这些是高吞吐写入系统的骨干。
- 可靠投递的队列: SQS、RabbitMQ。与流的保证不同。队列适合工作分发,流适合可重放的事件历史。
- 无状态消费者工作者: 独立处理事件并能水平扩展的服务。
- 幂等的存储写入: 这样重试是安全的,不会重复计数。
- Schema 注册表: 当你有数百个生产者和消费者时,Schema 演化会成为一个真正的问题。使用 Avro 或 Protobuf 配合注册表可以拯救你。
区分高级别回答的深入探讨和权衡
- 至少一次 vs 恰好一次 vs 至多一次。 大多数系统是至少一次配合幂等消费者。恰好一次是可能的(Kafka 事务),但成本高且有约束。要具体说明你的系统需要哪种以及为什么。
- 顺序保证和分区键。 Kafka 只保证分区内的顺序。如果你按 user_id 分区,该用户的所有事件有序,但不同用户的事件之间无序。选择错误的分区键会导致热点分区或顺序保障弱。
- 背压。 当消费者落后时会发生什么?生产者会减速吗?会溢出到磁盘吗?会丢弃事件吗?真实系统需要策略。
- 死信队列和毒消息。 如果单个格式错误的事件导致消费者崩溃,它会阻塞整个分区。死信队列让你跳过它,稍后调查。
- 重放和重新处理。 你能通过新的消费者重放最近 7 天的事件来回填功能吗?如果能,你就有了事件溯源架构。如果不能,你的管道就很脆弱。
- 保留成本。 在 Kafka 中存储 30 天的事件很昂贵。分层存储(近期在 Kafka,较旧的移入 S3)越来越标准。
面试陷阱:说「用 Kafka」而不解释分区键、消费者组语义、或者当消费者在半批处理中崩溃时会发生什么。
模式 3:实时扇出系统
属于此类的例子: 聊天(WhatsApp、Slack)、通知、实时比分、股票行情、协作文档(Google Docs、Figma)、直播弹幕、多人游戏状态、「设计 Slack」、「设计实时排行榜」。
要留意的信号
- 许多用户需要在几秒内看到更新,通常是亚秒级。
- 一个事件通常要扇出到数千或数百万客户端。
- 需要长连接或推送通道——这与请求-响应模式根本不同。
- 用户会来也会走。有些在线,有些离线需要追赶。
核心构建模块
- WebSockets、Server-Sent Events 或长轮询。 WebSockets 用于双向通信,SSE 仅服务端到客户端,长轮询作为 WebSockets 被阻挡时的后备方案。
- 实时网关层: 一个独立的有状态服务器层,保持连接打开。与你的无状态业务逻辑解耦。
- 扇出的发布/订阅: Kafka 主题、Redis pub/sub 或 NATS,将消息从发布者传递给所有相关的网关。
- 在线状态服务: 跟踪哪个用户连接到了哪个网关节点,以便正确路由消息。
- 消息历史存储: 这样重新连接的用户可以获取他们错过的消息。
区分高级别回答的深入探讨和权衡
- 每连接成本。 每个 WebSocket 连接占用内存和文件描述符。单个 Node.js 服务器可能处理 5 万到 10 万个连接;优化过的 Go 或 Rust 服务器可以达到一百万。在 Slack 的规模下,这一点极其重要。
- 按网关分片用户。 通常使用用户 ID 的一致性哈希。但是当持有 10 万个连接的网关宕机时会发生什么?这些用户同时重新连接——这是一种雪崩效应,可能拖垮相邻的网关。
- 扇出放大。 在一个有 1 万名成员的 Slack 频道中,一条消息会触发 1 万次投递。群组消息需要仔细设计,避免二次方的爆炸。
- 断开后的追赶。 当用户断开 30 秒后重新连接,他们需要每一条错过的消息吗?你需要每个频道有一个消息日志,支持基于游标的读取。
- 优雅降级。 如果实时层过载,你能否回退到 5 秒轮询?许多生产系统都这样做。
- 重连时的顺序和去重。 消息可能已被投递,连接在确认前断开,然后同一消息再次投递。客户端侧幂等性很重要。
面试陷阱:画一个单一的 WebSocket 服务器,不考虑它宕机时会发生什么,或者假设同一组服务器既处理连接又处理业务逻辑。
模式 4:批处理和分析系统
属于此类的例子: 报告仪表板、推荐管道、离线聚合、数据湖处理、BI 仪表板、「设计 YouTube 的观看次数计数器」、「设计计算日活跃用户的系统」。
要留意的信号
- 数据新鲜度可以是几分钟或几小时,而不是毫秒。「昨天的报告早上 9 点出来」是可以接受的。
- 数据量巨大,通常是跨多天的 TB 级。
- 查询扫描数据集的很大部分,而不是取出单行。
- 多个消费者需要相同的数据——财务、产品分析、机器学习训练、高管仪表板。
核心构建模块
- 数据湖存储: S3、GCS、ADLS。便宜、持久、读取时 schema。
- ETL 或 ELT 管道: Spark、Flink、Airflow、dbt、托管服务如 Glue 或 Dataflow。
- 列式查询引擎: BigQuery、Redshift、Snowflake、ClickHouse。列式存储使扫描数十亿行成为可能。
- 预计算聚合和物化视图: 这样仪表板查询即使在巨大表上也能快速执行。
- 工作流编排器: Airflow、Dagster、Prefect。管理作业之间的依赖关系。
区分高级别回答的深入探讨和权衡
- Lambda vs Kappa 架构。 Lambda 并行运行批处理和流处理并合并结果。Kappa 仅使用流处理,并通过重放进行重新处理。Lambda 运维复杂但更灵活;Kappa 更简单,但要求成熟的流处理设置。
- 延迟到达的数据。 如果因为移动客户端离线,昨天的事件今天到达,会发生什么?你的日聚合数据现在出错了。解决方案:Flink 中的水位线、允许延迟窗口更新、或者接受一定的不准确性,定义「数据质量」SLA。
- 批处理大小 vs 频率。 每 5 分钟运行一次带来近实时的新鲜度,但开销高。每天运行一次高效但有延迟。微批处理(每 15-30 分钟)通常是甜点区域。
- 分区策略。 按日期、租户或区域分区的表让查询快速且便宜。错误的分区使得一个查询应该扫描几 GB 却扫描了数 TB。
- 成本 vs 性能。 一个扫描 10TB 的 BigQuery 查询要花真金白银。查询优化、物化视图和分区不仅是性能问题,也是成本问题。
- 在线路径与分析路径的分离。 你的生产数据库绝不应该服务于分析查询。CDC(Debezium、Fivetran)将变更复制到数仓而不会影响生产。
面试陷阱:混淆分析路径和服务路径,或者不提及数据如何从运营系统流入数仓。
模式 5:搜索与过滤系统
属于此类的例子: 搜索栏、职位搜索、商品搜索、自动补全、日志搜索(Splunk、Datadog)、「设计小规模 Google 搜索」、「设计 Yelp 的餐厅搜索」、「设计 LinkedIn 的人员搜索」。
要留意的信号
- 用户通过文本、标签、过滤器或组合进行查询。
- 相关性和排序与原始速度同样重要。前 10 个结果就是一切。
- 写入流入索引,需要保持最新。
- 查询空间是开放的——你无法预计算每一个可能的查询。
核心构建模块
- 搜索索引: Elasticsearch、OpenSearch、Solr。基于 Lucene 的倒排索引。
- 采集管道: 将记录从数据源转换为搜索文档。通常是异步的,通过 Kafka。
- 查询层: 构建过滤器、评分、分页、分面聚合。
- 热门查询缓存: 热门搜索获取缓存的返回结果以减少负载。
- 排序信号服务: 结合文本相关性与业务信号(热度、时效性、个性化)。
区分高级别回答的深入探讨和权衡
- 索引延迟 vs 新鲜度。 新数据需要时间才能被搜索到——通常 Elasticsearch 是秒级,更大规模时更长。如果你需要「搜索 100 毫秒前刚发布的内容」,你需要不同的架构(内存中的侧索引,在查询时合并)。
- 分片策略。 按租户、按文档类型或按哈希分片。错误的分片会导致热点分片或分散-聚集查询模式,增加延迟。
- 拼写容错、词干处理和同义词。 开箱即用的搜索引擎不知道「iPhone 15 Pro」匹配「iphone15pro」或「苹果的最新手机」。语言处理很重要。
- 相关性调优。 纯 BM25 通常不够。你通常需要叠加业务信号(时效性、热度、点击率),有时还需要在其上叠加一个学习排序模型。
- 超过第 100 页的分页。 偏移分页在规模下会失效。使用基于游标的分页(Elasticsearch 中的 search_after)。
- 索引大小与成本。 一个 1 TB 的 Elasticsearch 集群保持热状态很昂贵。冷冻或冷层有助于处理较旧的数据。
- 重建索引。 当你更改 schema 或分析器时,你需要重建索引。对于数十亿文档,这是一个需要数天的操作,需要零停机策略(双写到新旧索引,然后切换)。
面试陷阱:建议「我们会用 SQL LIKE 查询」,或者不解释数据如何实际进入搜索索引。
模式 6:文件与媒体存储系统
属于此类的例子: 照片分享、视频上传、文档存储、头像服务、「设计 Instagram」、「设计 Dropbox」、「设计 YouTube 上传」。
要留意的信号
- 大型二进制对象,通常数 MB 到 GB。
- 需要转换:缩略图、转码、格式转换、病毒扫描。
- 访问模式从非常热(今天的热门视频)到非常冷。
相似文章
@tom_doerr:基于畅销指南的系统设计面试笔记 https://github.com/liquidslr/system-design-notes…
一个 GitHub 仓库,包含基于 Alex Xu 畅销书的综合系统设计面试笔记,涵盖扩展性、一致性哈希和分布式系统等主题。
@system_monarch:我有12年经验,现任Atlassian首席工程师,我目睹过并发把无数初级工程师吓得魂飞魄散……
一位Atlassian首席工程师指出,并发概念让初级工程师闻风丧胆,并成为后端面试中最令人畏惧的环节。
@heynavtoor: 亚历克斯·徐的《系统设计面试》是技术招聘中最受推荐的书。第1卷:亚马逊上39.99美元。第2卷:4……
一位AWS工程师创建了免费的、结构化的笔记,总结了亚历克斯·徐的《系统设计面试》两卷的内容,可在GitHub和Pagefy.io网站上获取。
jwasham/coding-interview-university
一份全面的、为期数月的软件工程面试学习计划,针对大型科技公司,最初由John Washam创建并被开发者社区广泛采用。
@LearnWithBrij: 系统设计大师 系统设计大师树 │ ├── 1. 基础 │ ├── 什么是系统设计 │ ├── 功能需求…
一个全面的系统设计大师树,涵盖从基础知识到实际应用的各个方面,包括架构模式、数据库、缓存、消息系统、API设计和部署策略。旨在作为软件工程师的结构化学习指南。