Web Analytics

system-design-primer

⭐ 318813 stars Simplified Chinese by donnemartin

English日本語简体中文繁體中文 | العَرَبِيَّة‎বাংলাPortuguês do BrasilDeutschελληνικάעבריתItaliano한국어فارسیPolskiрусский языкEspañolภาษาไทยTürkçetiếng ViệtFrançais | Add Translation 帮助翻译本指南!

系统设计入门


动机

学习如何设计大规模系统。
>
准备系统设计面试。

学习如何设计大规模系统

学习如何设计可扩展系统将帮助你成为更优秀的工程师。

系统设计是一个广泛的主题。网络上散布着大量关于系统设计原则的资源

本仓库是一个有组织的资源集合,帮助你学习如何构建大规模系统。

向开源社区学习

这是一个持续更新的开源项目。

欢迎贡献

准备系统设计面试

除了编码面试,系统设计是许多科技公司技术面试流程中的必备环节

练习常见的系统设计面试题,并将你的结果与示例解答进行比较:讨论、代码和图表。

面试准备的其他主题:

Anki 闪卡


提供的 Anki 闪卡套装 使用间隔重复帮助你记忆关键的系统设计概念。

非常适合在移动中使用。

编程资源:互动编程挑战

想找资源帮助准备编程面试吗?


看看姊妹仓库 互动编程挑战,其中包含额外的 Anki 套装:

贡献

从社区中学习。

欢迎提交拉取请求帮助:

需要润色的内容放置在开发中

请查看贡献指南

系统设计主题索引

各种系统设计主题的总结,包括优缺点。一切都是权衡
>
每个章节包含指向更深入资源的链接。


学习指南

根据你的面试时间线建议复习的主题(短期、中期、长期)。

Imgur

问:面试时,我需要知道这里的所有内容吗?

答:不,你不需要知道这里的所有内容来准备面试

面试时被问到的内容取决于以下变量:

经验更丰富的候选人通常被期望对系统设计了解更多。架构师或团队负责人可能被期望比普通贡献者知道更多。顶级科技公司很可能有一轮或多轮设计面试。

从广泛开始,在少数几个领域深入。了解一些关键系统设计主题会很有帮助。根据你的时间安排、经验、面试职位和面试公司调整以下指南。

| | 短期 | 中期 | 长期 | |---|---|---|---| | 通读系统设计主题,对系统如何工作有一个广泛的了解 | :+1: | :+1: | :+1: | | 阅读你面试公司的公司工程博客中的几篇文章 | :+1: | :+1: | :+1: | | 阅读几篇真实世界架构 | :+1: | :+1: | :+1: | | 复习如何处理系统设计面试题 | :+1: | :+1: | :+1: | | 练习带有解答的系统设计面试题 | 一些 | 许多 | 大部分 | | 练习带有解答的面向对象设计面试题 | 一些 | 许多 | 大部分 | | 复习额外的系统设计面试题 | 一些 | 许多 | 大部分 |

如何处理系统设计面试题

如何解决系统设计面试题。

系统设计面试是一个开放式对话。你需要主导它。

你可以使用以下步骤来引导讨论。为了帮助巩固这个过程,使用以下步骤完成带有解答的系统设计面试题部分。

第一步:概述用例、约束和假设

收集需求并确定问题范围。提问以澄清用例和约束。讨论假设。

第二步:创建高级设计

概述一个包含所有重要组件的高级设计。

第3步:设计核心组件

深入讨论每个核心组件的细节。例如,如果你被要求设计一个URL缩短服务,可以讨论:

第4步:扩展设计

识别并解决瓶颈问题,考虑约束条件。例如,你是否需要以下方案来解决可扩展性问题?

讨论潜在的解决方案和权衡。所有设计都有权衡。使用可扩展系统设计原则来解决瓶颈问题。

估算计算

你可能需要手动做一些估算。请参考附录中的以下资源:

来源和进一步阅读

查看以下链接以更好地了解预期内容:

系统设计面试题及解决方案

常见系统设计面试题,附示例讨论、代码及图表。
>
解决方案链接指向 solutions/ 文件夹中的内容。

| 问题 | | |---|---| | 设计 Pastebin.com(或 Bit.ly) | 解决方案 | | 设计 Twitter 时间线和搜索(或 Facebook 动态和搜索) | 解决方案 | | 设计一个网络爬虫 | 解决方案 | | 设计 Mint.com | 解决方案 | | 设计社交网络的数据结构 | 解决方案 | | 设计搜索引擎的键值存储 | 解决方案 | | 设计亚马逊按类别的销售排名功能 | 解决方案 | | 设计一个可扩展至数百万用户的 AWS 系统 | 解决方案 | | 添加系统设计题目 | 贡献 |

设计 Pastebin.com(或 Bit.ly)

查看练习及解决方案

Imgur

设计 Twitter 时间线和搜索(或 Facebook 动态和搜索)

查看练习及解决方案

Imgur

设计一个网络爬虫

查看练习及解决方案

Imgur

Design Mint.com

View exercise and solution

Imgur

Design the data structures for a social network

View exercise and solution

Imgur

Design a key-value store for a search engine

View exercise and solution

Imgur

Design Amazon's sales ranking by category feature

View exercise and solution

Imgur

Design a system that scales to millions of users on AWS

View exercise and solution

Imgur

Object-oriented design interview questions with solutions

Common object-oriented design interview questions with sample discussions, code, and diagrams.
>
Solutions linked to content in the solutions/ folder.

>Note: This section is under development

| Question | | |---|---| | 设计哈希映射 | 解决方案 | | 设计最近最少使用缓存 | 解决方案 | | 设计呼叫中心 | 解决方案 | | 设计一副牌 | 解决方案 | | 设计停车场 | 解决方案 | | 设计聊天服务器 | 解决方案 | | 设计循环数组 | 贡献 | | 添加面向对象设计问题 | 贡献 |

系统设计主题:从这里开始

系统设计新手?

首先,你需要对常见原则有基本了解,学习它们是什么、如何使用以及它们的优缺点。

第一步:观看可扩展性视频讲座

哈佛可扩展性讲座

第二步:阅读可扩展性文章

可扩展性

接下来的步骤

接下来,我们将关注高级权衡:

请记住,一切都是权衡

然后我们将深入探讨更具体的主题,如 DNS、CDN 和负载均衡器。

性能与可扩展性

如果服务能够随着资源的增加而按比例提升性能,则该服务是可扩展的。通常,提高性能意味着处理更多的工作单位,但也可以是处理更大的工作单位,例如数据集增长时。1

从另一个角度看性能与可扩展性:

资料来源及进一步阅读

延迟与吞吐量

延迟 是执行某个动作或产生某个结果所需的时间。

吞吐量 是单位时间内完成的此类动作或产生的结果数量。

通常,你应当以最大吞吐量可接受的延迟为目标。

资料来源及进一步阅读

可用性与一致性

CAP 定理


来源:CAP 定理再访

在分布式计算系统中,你只能支持以下三项保证中的两项:

网络不可靠,因此你需要支持分区容错。你需要在一致性和可用性之间做出软件上的权衡。

#### CP - 一致性和分区容错性

等待分区节点的响应可能导致超时错误。如果业务需求要求原子读写,CP 是一个不错的选择。

#### AP - 可用性和分区容错性

响应返回任何节点上最容易获得的数据版本,可能不是最新的。当分区恢复时,写操作可能需要一定时间传播。

当业务需要允许最终一致性或系统需要在外部错误发生时继续工作时,AP 是一个不错的选择。

来源及进一步阅读

一致性模式

当存在多个相同数据副本时,我们面临如何同步它们以保证客户端数据一致性的选择。回想CAP 定理中一致性的定义——每次读取都能获得最近的写入内容,或返回错误。

弱一致性

写入后,读取可能能看到也可能看不到写入内容。采取的是尽力而为的方式。

这种方式见于如 memcached 这类系统。弱一致性在实时应用场景中表现良好,如 VoIP、视频聊天和实时多人游戏。例如,如果你在通话中短暂失去信号,恢复连接后不会听到信号丢失期间的内容。

最终一致性

写入后,读取最终会看到该写入(通常在毫秒级内)。数据是异步复制的。

这种方法见于 DNS 和电子邮件等系统。最终一致性在高可用系统中效果良好。

强一致性

写入后,读取会立即看到该写入。数据是同步复制的。

这种方法见于文件系统和关系型数据库系统。强一致性适用于需要事务的系统。

来源及进一步阅读

可用性模式

支持高可用性的两种互补模式:故障切换复制

故障切换

#### 主备模式

在主备故障切换中,主服务器与待命的备份服务器之间会发送心跳信号。若心跳中断,备份服务器接管主服务器的 IP 地址并恢复服务。

停机时间长短取决于备份服务器是处于“热”待命状态还是需要从“冷”待命启动。仅主服务器处理流量。

主备故障切换也可称为主从故障切换。

#### 主主模式

在主主模式中,两个服务器都在管理流量,分摊负载。

如果服务器面向公网,DNS 需要知道两个服务器的公网 IP 地址;如果服务器面向内部,应用逻辑需要知道两个服务器。

主主故障切换也可称为主主复制故障切换。

缺点:故障切换

复制

#### 主从和主主

此主题在数据库部分中有更详细的讨论:

可用性数字化

可用性通常通过正常运行时间(或停机时间)作为服务可用时间的百分比来量化。可用性通常用“9”的个数来衡量——具有99.99%可用性的服务被描述为有四个9。

#### 99.9% 可用性 - 三个9

| 时长 | 可接受的停机时间 | |--------------------|--------------------| | 每年停机时间 | 8小时45分57秒 | | 每月停机时间 | 43分49.7秒 | | 每周停机时间 | 10分4.8秒 | | 每日停机时间 | 1分26.4秒 |

#### 99.99% 可用性 - 四个9

| 时长 | 可接受的停机时间 | |--------------------|--------------------| | 每年停机时间 | 52分35.7秒 | | 每月停机时间 | 4分23秒 | | 每周停机时间 | 1分5秒 | | 每日停机时间 | 8.6秒 |

#### 并行与串行的可用性

如果服务由多个易失败的组件组成,服务的整体可用性取决于这些组件是串行还是并行。

###### 串行

当两个可用性低于100%的组件依次连接时,总体可用性会降低:

Availability (Total) = Availability (Foo) * Availability (Bar)
如果 FooBar 各自的可用性都是 99.9%,它们顺序组合的总可用性将是 99.8%。

###### 并行

当两个可用性低于 100% 的组件并行时,总体可用性会提高:

Availability (Total) = 1 - (1 - Availability (Foo)) * (1 - Availability (Bar))
如果 FooBar 都有 99.9% 的可用性,那么它们并联的总可用性将是 99.9999%。

域名系统


来源:DNS 安全演示文稿

域名系统(DNS)将诸如 www.example.com 这样的域名转换为 IP 地址。

DNS 是分层的,顶层有少数权威服务器。您的路由器或 ISP 提供关于查找时应联系哪个 DNS 服务器的信息。低级 DNS 服务器会缓存映射,由于 DNS 传播延迟,缓存可能变得过时。DNS 结果也可以由您的浏览器或操作系统缓存一段时间,这取决于生存时间(TTL)

诸如 CloudFlareRoute 53 等服务提供托管的 DNS 服务。一些 DNS 服务可以通过多种方法路由流量:

缺点:DNS

资源及进一步阅读

内容分发网络


来源:为什么使用 CDN

内容分发网络(CDN)是一种全球分布的代理服务器网络,从更靠近用户的位置提供内容。通常,静态文件如 HTML/CSS/JS、照片和视频由 CDN 提供,尽管一些 CDN 如亚马逊的 CloudFront 支持动态内容。网站的 DNS 解析会告诉客户端联系哪个服务器。

从 CDN 提供内容可以通过两种方式显著提升性能:

推送型 CDN

推送型 CDN 在服务器内容发生变化时接收新内容。你需要全权负责内容的提供,直接上传到 CDN 并重写 URL 指向 CDN。你可以配置内容的过期时间和更新时间。内容仅在新建或更改时上传,最大限度减少流量,同时最大化存储。

流量较小或内容不常更新的网站非常适合推送型 CDN。内容只需上传到 CDN 一次,而不是定期重新拉取。

拉取型 CDN

拉取型 CDN 在首个用户请求内容时从你的服务器抓取新内容。你将内容保留在服务器上,并重写 URL 指向 CDN。这导致第一次请求较慢,直到内容缓存到 CDN 上。

生存时间 (TTL) 决定内容缓存的时长。拉取型 CDN 最大化减少 CDN 上的存储空间,但如果文件过期且尚未实际更改就被拉取,会产生冗余流量。

流量较大的站点适合拉取型 CDN,因为流量更均匀分布,只有最近请求的内容保留在 CDN 上。

缺点:CDN

来源及进一步阅读

负载均衡器


来源:可扩展系统设计模式

负载均衡器将传入的客户端请求分发到计算资源,如应用服务器和数据库。在每种情况下,负载均衡器将计算资源的响应返回给相应的客户端。负载均衡器有效于:

负载均衡器可以通过硬件(昂贵)或软件实现,如 HAProxy。

其他好处包括:

为防止故障,通常会设置多个负载均衡器,以 主动-被动主动-主动 模式运行。

负载均衡器可以基于多种指标路由流量,包括:

第 4 层负载均衡

第 4 层负载均衡器查看传输层的信息来决定如何分配请求。通常,这涉及报头中的源地址、目标 IP 地址和端口,但不包括数据包的内容。第 4 层负载均衡器将网络数据包转发到上游服务器,并执行网络地址转换 (NAT)

第 7 层负载均衡

第7层负载均衡器查看应用层来决定如何分配请求。 这可能涉及头部、消息和Cookie的内容。 第7层负载均衡器终止网络流量,读取消息,做出负载均衡决策,然后打开与选定服务器的连接。 例如,第7层负载均衡器可以将视频流量引导到托管视频的服务器,同时将更敏感的用户计费流量引导到安全加固的服务器。

以灵活性为代价,第4层负载均衡所需的时间和计算资源比第7层少,尽管在现代通用硬件上性能影响可能很小。

水平扩展

负载均衡器还可以帮助进行水平扩展,提高性能和可用性。 使用通用机器进行横向扩展比使用更昂贵硬件上的单个服务器进行纵向扩展(称为纵向扩展)更具成本效益且可用性更高。 招聘在通用硬件上工作的技术人才也比专门的企业系统更容易。

#### 缺点:水平扩展

缺点:负载均衡器

来源及进一步阅读

反向代理(网页服务器)


来源:维基百科

反向代理是一种网络服务器,它集中管理内部服务并向公众提供统一接口。客户端的请求被转发到能够处理该请求的服务器,反向代理再将服务器的响应返回给客户端。

额外的好处包括:

负载均衡器与反向代理

缺点:反向代理

来源及进一步阅读

应用层


来源:架构可扩展系统简介

将 Web 层与应用层(也称为平台层)分离,可以让你独立地扩展和配置这两个层。添加新的 API 会导致添加应用服务器,而不一定需要增加额外的 Web 服务器。单一职责原则主张构建小型且自治的服务协同工作。拥有小团队和小服务可以更积极地规划快速增长。

应用层的工作者还帮助实现了异步

微服务

与此相关的是微服务,它可以被描述为一套独立部署的小型模块化服务。每个服务运行一个独特的进程,并通过一个定义良好、轻量级的机制进行通信,以实现业务目标。1

例如,Pinterest 可能有以下微服务:用户资料、关注者、动态、搜索、照片上传等。

服务发现

诸如ConsulEtcdZookeeper等系统可以帮助服务通过跟踪注册的名称、地址和端口找到彼此。健康检查帮助验证服务完整性,通常通过HTTP端点完成。Consul 和 Etcd 都内置了key-value 存储,这对于存储配置值和其他共享数据很有用。

缺点:应用层

来源及进一步阅读

数据库


来源:扩展到第一个一千万用户

关系型数据库管理系统(RDBMS)

关系型数据库如 SQL 是以表格形式组织的数据项集合。

ACID 是关系型数据库事务的一组属性。

有许多技术可以扩展关系型数据库:主从复制主主复制联合分片反范式设计SQL 调优

#### 主从复制

主节点处理读写操作,将写操作复制到一个或多个从节点,从节点仅处理读操作。从节点也可以以树状结构复制到其他从节点。如果主节点离线,系统可以继续以只读模式运行,直到从节点被提升为主节点或新主节点被配置。


来源:可扩展性、可用性、稳定性模式

##### 缺点:主从复制

#### 主主复制

两个主节点都处理读写操作,并在写操作上相互协调。如果任一主节点宕机,系统仍可继续进行读写操作。


来源:可扩展性、可用性、稳定性模式

##### 缺点:主主复制

##### 缺点:复制

##### 复制的来源和进一步阅读

#### 联邦


来源:扩展到你的第一个1000万用户

联邦(或功能分区)按功能划分数据库。例如,不使用单一的整体数据库,而是拥有三个数据库:论坛用户产品,从而减少每个数据库的读写流量,降低复制延迟。较小的数据库意味着更多数据可以放入内存,进而因缓存局部性改进而提高缓存命中率。没有单一的中央主节点串行写入,写入可以并行进行,提高吞吐量。

##### 联邦的缺点

##### 联邦的来源和进一步阅读

#### 分片


来源:可扩展性、可用性、稳定性、模式

分片(Sharding)将数据分布到不同的数据库中,使得每个数据库只能管理数据的一个子集。以用户数据库为例,随着用户数量的增加,集群中会添加更多的分片。

联合的优点类似,分片带来更少的读写流量、更少的复制以及更多的缓存命中。索引大小也减少,通常这能提升性能,加快查询速度。如果某个分片宕机,其他分片仍然可以正常工作,不过你需要添加某种复制机制以避免数据丢失。像联合一样,没有单一中央主库来串行化写操作,允许你并行写入,提高吞吐量。

常见的用户表分片方式是通过用户的姓氏首字母或用户的地理位置。

##### 缺点:分片

##### 来源及扩展阅读:分片

#### 反规范化

反规范化试图以牺牲部分写性能为代价来提升读性能。在多个表中写入冗余数据副本以避免昂贵的连接操作。一些关系型数据库管理系统(如PostgreSQL和Oracle)支持物化视图,它们负责存储冗余信息并保持副本一致。

一旦数据通过联合分片等技术分布开来,跨数据中心管理连接操作的复杂性进一步增加。反规范化可能避免了这种复杂连接的需求。

在大多数系统中,读操作远远多于写操作,比例可能达到100:1甚至1000:1。一次复杂的数据库连接查询可能非常昂贵,花费大量时间在磁盘操作上。

##### 缺点:反规范化

###### 来源及扩展阅读:反规范化

#### SQL 调优

SQL 调优是一个广泛的话题,已经有许多书籍作为参考。

进行基准测试性能分析来模拟并发现瓶颈非常重要。

基准测试和性能分析可能会指引您进行以下优化。

##### 收紧数据库模式

##### 使用合适的索引

##### 避免昂贵的连接

##### 分区表

##### 调优查询缓存

##### 来源及进一步阅读:SQL调优

NoSQL

NoSQL 是一组以键值存储文档存储宽列存储图数据库形式表示的数据项。数据是非规范化的,连接通常在应用代码中完成。大多数 NoSQL 存储缺乏真正的 ACID 事务,偏向于最终一致性

BASE 常用来描述 NoSQL 数据库的特性。与CAP 定理相比,BASE 选择可用性而非一致性。

除了在SQL或NoSQL之间选择外,了解哪种类型的 NoSQL 数据库最适合您的用例也很有帮助。我们将在下一节回顾键值存储文档存储宽列存储图数据库

#### 键值存储

抽象:哈希表

键值存储通常允许 O(1) 的读写,通常由内存或 SSD 支持。数据存储可以保持键的字典序,从而高效检索键范围。键值存储可以允许与值一起存储元数据。

键值存储提供高性能,常用于简单数据模型或快速变化的数据,如内存缓存层。由于它们仅提供有限的操作集,若需要额外操作,复杂性将转移到应用层。

键值存储是更复杂系统的基础,例如文档存储,某些情况下也是图数据库。

##### 来源及进一步阅读:键值存储

#### 文档存储

抽象:键值存储,文档作为值存储

文档存储以文档(XML、JSON、二进制等)为中心,文档存储给定对象的所有信息。文档存储提供基于文档内部结构的 API 或查询语言进行查询。注意,许多键值存储包含处理值元数据的功能,模糊了这两种存储类型之间的界限。

根据底层实现,文档通过集合、标签、元数据或目录进行组织。尽管文档可以被组织或分组,但文档的字段可能彼此完全不同。

一些文档存储如 MongoDBCouchDB 也提供类似 SQL 的语言来执行复杂查询。DynamoDB 同时支持键值和文档。

文档存储提供高度灵活性,常用于处理偶尔变更的数据。

##### 来源及进一步阅读:文档存储

#### 宽列存储


来源:SQL & NoSQL,简史

抽象:嵌套映射 ColumnFamily>

宽列存储的数据基本单元是列(名称/值对)。列可以分组到列族中(类似于 SQL 表)。超级列族进一步对列族进行分组。您可以通过行键独立访问每个列,具有相同行键的列组成一行。每个值包含时间戳,用于版本控制和冲突解决。

Google 推出了 Bigtable 作为首个宽列存储,影响了开源的 HBase(常用于 Hadoop 生态系统)和 Facebook 的 Cassandra。BigTable、HBase 和 Cassandra 等存储按字典序维护键,支持高效检索特定键范围。

宽列存储提供高可用性和高扩展性,常用于超大数据集。

##### 来源及进一步阅读:宽列存储

#### 图数据库


来源:图数据库

抽象:图

在图数据库中,每个节点是一个记录,每条弧是两个节点之间的关系。图数据库针对具有许多外键或多对多关系的复杂关系进行了优化。

图数据库为具有复杂关系的数据模型(如社交网络)提供高性能。它们相对较新,尚未被广泛使用;可能更难找到开发工具和资源。许多图只能通过REST API访问。

##### 来源及进一步阅读:图

#### 来源及进一步阅读:NoSQL

SQL 或 NoSQL


来源:从关系型数据库到 NoSQL 的转变

SQL 的原因:

NoSQL 的原因:

适合 NoSQL 的示例数据:

##### 来源及进一步阅读:SQL 或 NoSQL

缓存


来源:可扩展系统设计模式

缓存可以提高页面加载速度,并减少服务器和数据库的负载。在此模型中,调度器会首先查找请求是否已被处理过,尝试找到之前的结果返回,以节省实际执行的时间。

数据库通常受益于跨分区的读写均匀分布。热门项目可能会导致分布不均,造成瓶颈。在数据库前放置缓存可以帮助吸收不均匀的负载和流量峰值。

客户端缓存

缓存可以位于客户端(操作系统或浏览器)、服务器端或独立的缓存层。

CDN 缓存

内容分发网络(CDN)被视为一种缓存。

Web 服务器缓存

反向代理和如 Varnish 等缓存可以直接提供静态和动态内容。Web 服务器也可以缓存请求,返回响应而无需联系应用服务器。

数据库缓存

数据库通常在默认配置中包含一定程度的缓存,针对通用使用场景进行优化。针对特定使用模式调整这些设置可以进一步提升性能。

应用缓存

内存缓存如 Memcached 和 Redis 是应用与数据存储之间的键值存储。由于数据存储在内存中,比典型的基于磁盘存储的数据库更快。内存容量有限,因此缓存失效算法如最近最少使用(LRU))可以帮助失效“冷”条目,保持“热”数据在内存中。

Redis 具有以下额外功能:

缓存可以存在多个层级,归于两大类:数据库查询对象

通常,应尽量避免基于文件的缓存,因为这会增加克隆和自动扩展的难度。

数据库查询级别的缓存

每当查询数据库时,将查询哈希作为键并将结果存储到缓存中。此方法存在过期问题:

对象级别的缓存

将数据视为对象,类似于应用程序代码的处理方式。让应用程序将数据库中的数据集组装成类实例或数据结构:

建议缓存的内容:

何时更新缓存

由于缓存中只能存储有限的数据,需要确定哪种缓存更新策略最适合您的用例。

#### Cache-aside


来源:从缓存到内存数据网格简介

应用程序负责从存储中读取和写入。缓存不直接与存储交互。应用程序执行以下操作:

def get_user(self, user_id):
    user = cache.get("user.{0}", user_id)
    if user is None:
        user = db.query("SELECT * FROM users WHERE user_id = {0}", user_id)
        if user is not None:
            key = "user.{0}".format(user_id)
            cache.set(key, json.dumps(user))
    return user
Memcached 通常以这种方式使用。

随后的缓存数据读取速度很快。Cache-aside 也称为懒加载。仅缓存被请求的数据,避免缓存被未请求的数据填满。

##### 缺点:cache-aside

#### 写直达(Write-through)


来源:可扩展性、可用性、稳定性模式

应用程序将缓存作为主要数据存储,读写数据时都操作缓存,缓存负责读写数据库:

应用程序代码:

set_user(12345, {"foo":"bar"})
缓存代码:

def set_user(user_id, values):
    user = db.query("UPDATE Users WHERE id = {0}", user_id, values)
    cache.set(user_id, user)
写入直达(Write-through)由于写操作而整体较慢,但随后读取刚写入的数据速度很快。用户在更新数据时通常对延迟更为容忍,而读取数据时则不然。缓存中的数据不会过时。

##### 缺点:写入直达

#### 写入回写(Write-behind / Write-back)


来源:可扩展性、可用性、稳定性模式

在写入回写中,应用程序执行以下操作:

##### 缺点:写入回写

#### 预刷新(Refresh-ahead)


来源:从缓存到内存数据网格

您可以配置缓存,在条目过期前自动刷新任何最近访问的缓存条目。

如果缓存能够准确预测未来可能需要的条目,预刷新相比读取穿透可以减少延迟。

##### 缺点:预刷新

缺点:缓存

来源及进一步阅读

异步性


来源:系统架构扩展简介

异步工作流有助于减少对昂贵操作的请求时间,这些操作本来会同步执行。它们还可以通过提前完成耗时工作(如定期数据聚合)来发挥作用。

消息队列

消息队列用于接收、存储和传递消息。如果操作太慢无法同步执行,可以使用消息队列,流程如下:

用户不会被阻塞,任务在后台处理。此期间,客户端可以执行少量处理,使任务看起来已完成。例如,发送推文时,推文可以立即显示在时间线上,但实际传递给所有关注者可能需要一些时间。

Redis 是一个简单的消息代理,但消息可能丢失。

RabbitMQ 很受欢迎,但需要适应 'AMQP' 协议并自行管理节点。

Amazon SQS 是托管服务,但可能存在高延迟,并且消息可能会被重复传递。

任务队列

任务队列接收任务及其相关数据,执行任务,然后传递结果。它们支持调度,可用于在后台运行计算密集型作业。

Celery 支持调度,主要支持 Python。

背压

如果队列开始显著增长,队列大小可能超过内存,导致缓存未命中、磁盘读取,甚至性能更慢。背压 通过限制队列大小来帮助维持高吞吐率和队列中作业的良好响应时间。一旦队列满了,客户端会收到服务器繁忙或 HTTP 503 状态码,提示稍后重试。客户端可以在稍后时间重试请求,可能采用指数退避

缺点:异步性

来源及进一步阅读

通信


来源:OSI 七层模型

超文本传输协议 (HTTP)

HTTP 是一种在客户端和服务器之间编码和传输数据的方法。它是一种请求/响应协议:客户端发出请求,服务器发出包含相关内容和请求完成状态信息的响应。HTTP 是自包含的,允许请求和响应通过执行负载均衡、缓存、加密和压缩的多个中间路由器和服务器。

一个基本的 HTTP 请求由动词(方法)和资源(端点)组成。以下是常见的 HTTP 动词:

| 动词 | 描述 | 幂等* | 安全 | 可缓存 | |---|---|---|---|---| | GET | 读取资源 | 是 | 是 | 是 | | POST | 创建资源或触发处理数据的进程 | 否 | 否 | 如果响应包含新鲜度信息,则是 | | PUT | 创建或替换资源 | 是 | 否 | 否 | | PATCH | 部分更新资源 | 否 | 否 | 如果响应包含新鲜度信息,则是 | | DELETE | 删除资源 | 是 | 否 | 否 |

HTTP 是一种应用层协议,依赖于诸如 TCPUDP 之类的底层协议。

#### 来源及进一步阅读:HTTP

传输控制协议(TCP)


来源:如何制作多人游戏

TCP 是基于 IP 网络 的面向连接的协议。连接通过 握手 建立和终止。所有发送的数据包都保证以原始顺序且无损坏地到达目的地,通过:

如果发送方没有收到正确响应,它将重新发送数据包。如果多次超时,连接将被断开。TCP 还实现了 流量控制) 和 拥塞控制。这些保障会导致延迟,并且通常比 UDP 传输效率低。

为了确保高吞吐量,Web 服务器可以保持大量 TCP 连接打开,导致高内存使用。在 Web 服务器线程与例如 memcached 服务器之间保持大量打开连接可能代价高昂。除了在适用情况下切换到 UDP,使用 连接池 也能有所帮助。

TCP 适用于需要高可靠性但对时间要求不高的应用。一些例子包括 Web 服务器、数据库信息、SMTP、FTP 和 SSH。

在以下情况下使用 TCP 而非 UDP:

用户数据报协议(UDP)


来源:如何制作多人游戏

UDP 是无连接的。数据报(类似于数据包)仅在数据报级别被保证。数据报可能乱序到达目的地,甚至根本无法到达。UDP 不支持拥塞控制。由于缺乏 TCP 提供的保证,UDP 通常更高效。

UDP 可以广播,向子网内所有设备发送数据报。这在 DHCP 中非常有用,因为客户端尚未收到 IP 地址,因此 TCP 无法在没有 IP 地址的情况下进行流传输。

UDP 可靠性较低,但在 VoIP、视频聊天、流媒体和实时多人游戏等实时应用中表现良好。

在以下情况下使用 UDP 而非 TCP:

#### 来源及进一步阅读:TCP 和 UDP

远程过程调用(RPC)


来源:破解系统设计面试

在 RPC 中,客户端使一个过程在不同的地址空间上执行,通常是远程服务器。该过程的编码方式就像本地过程调用一样,抽象了如何与服务器通信的细节,客户端程序无需关心。远程调用通常比本地调用更慢且不那么可靠,因此区分 RPC 调用和本地调用是有帮助的。流行的 RPC 框架包括 ProtobufThriftAvro

RPC 是一种请求-响应协议:

示例RPC调用:

GET /someoperation?data=anId

POST /anotheroperation { "data":"anId"; "anotherdata": "another value" }

RPC 专注于暴露行为。RPC 通常用于内部通信以提升性能,因为你可以手工编写本地调用以更好地适应你的用例。

选择本地库(即 SDK)时:

遵循 REST 的 HTTP API 更常用于公共 API。

#### 缺点:RPC

表现层状态转移 (REST)

REST 是一种架构风格,强制执行客户端/服务器模型,其中客户端对服务器管理的一组资源进行操作。服务器提供资源的表现形式和可以操作或获取新资源表现形式的动作。所有通信必须是无状态且可缓存的。

RESTful 接口有四个特性:

REST 调用示例:

GET /someresources/anId

PUT /someresources/anId {"anotherdata": "another value"}

REST 专注于数据的暴露。它最大限度地减少了客户端/服务器之间的耦合,通常用于公共 HTTP API。REST 通过 URI、通过头部的表示 以及通过诸如 GET、POST、PUT、DELETE 和 PATCH 之类的动词来使用更通用和统一的方法暴露资源。由于是无状态的,REST 非常适合水平扩展和分区。

#### 缺点:REST

RPC 和 REST 调用比较

| 操作 | RPC | REST | |---|---|---| | 注册 | POST /signup | POST /persons | | 辞职 | POST /resign
{
"personid": "1234"
} | DELETE /persons/1234 | | 读取某人 | GET /readPerson?personid=1234 | GET /persons/1234 | | 读取某人的物品列表 | GET /readUsersItemsList?personid=1234 | GET /persons/1234/items | | 向某人物品列表添加物品 | POST /addItemToUsersItemsList
{
"personid": "1234";
"itemid": "456"
} | POST /persons/1234/items
{
"itemid": "456"
} | | 更新物品 | POST /modifyItem
{
"itemid": "456";
"key": "value"
} | PUT /items/456
{
"key": "value"
} | | 删除物品 | POST /removeItem
{
"itemid": "456"
} | DELETE /items/456 |

来源:你真的知道为什么你更喜欢 REST 而不是 RPC 吗

#### 来源及进一步阅读:REST 和 RPC

安全

本节内容需要更新。欢迎贡献

安全是一个广泛的话题。除非你有丰富的经验、安全背景,或者申请的职位需要安全知识,否则你可能只需要了解基础知识:

来源及进一步阅读

附录

有时你会被要求做“信封背面”的估算。例如,你可能需要确定从磁盘生成100个图像缩略图需要多长时间,或者一个数据结构会占用多少内存。二的幂表每个程序员都应了解的延迟数字是很好的参考资料。

二的幂表

Power           Exact Value         Approx Value        Bytes
---------------------------------------------------------------
7                             128
8                             256
10                           1024   1 thousand           1 KB
16                         65,536                       64 KB
20                      1,048,576   1 million            1 MB
30                  1,073,741,824   1 billion            1 GB
32                  4,294,967,296                        4 GB
40              1,099,511,627,776   1 trillion           1 TB

#### 来源及进一步阅读

每个程序员都应了解的延迟数字

Latency Comparison Numbers
--------------------------
L1 cache reference                           0.5 ns
Branch mispredict                            5   ns
L2 cache reference                           7   ns                      14x L1 cache
Mutex lock/unlock                           25   ns
Main memory reference                      100   ns                      20x L2 cache, 200x L1 cache
Compress 1K bytes with Zippy            10,000   ns       10 us
Send 1 KB bytes over 1 Gbps network     10,000   ns       10 us
Read 4 KB randomly from SSD*           150,000   ns      150 us          ~1GB/sec SSD
Read 1 MB sequentially from memory     250,000   ns      250 us
Round trip within same datacenter      500,000   ns      500 us
Read 1 MB sequentially from SSD*     1,000,000   ns    1,000 us    1 ms  ~1GB/sec SSD, 4X memory
HDD seek                            10,000,000   ns   10,000 us   10 ms  20x datacenter roundtrip
Read 1 MB sequentially from 1 Gbps  10,000,000   ns   10,000 us   10 ms  40x memory, 10X SSD
Read 1 MB sequentially from HDD     30,000,000   ns   30,000 us   30 ms 120x memory, 30X SSD
Send packet CA->Netherlands->CA    150,000,000   ns  150,000 us  150 ms

Notes ----- 1 ns = 10^-9 seconds 1 us = 10^-6 seconds = 1,000 ns 1 ms = 10^-3 seconds = 1,000 us = 1,000,000 ns

基于以上数字的实用指标:

#### 延迟数字可视化

#### 来源及进一步阅读

额外的系统设计面试题

常见系统设计面试题,附带解决资源链接。

| 问题 | 参考资料 | |---|---| | 设计一个类似 Dropbox 的文件同步服务 | youtube.com | | 设计一个类似 Google 的搜索引擎 | queue.acm.org
stackexchange.com
ardendertat.com
stanford.edu | | 设计一个类似 Google 的可扩展网络爬虫 | quora.com | | 设计 Google 文档 | code.google.com
neil.fraser.name | | 设计一个类似 Redis 的键值存储 | slideshare.net | | 设计一个类似 Memcached 的缓存系统 | slideshare.net | | 设计一个类似亚马逊的推荐系统 | hulu.com
ijcai13.org | | 设计一个类似 Bitly 的短链接系统 | n00tc0d3r.blogspot.com | | 设计一个类似 WhatsApp 的聊天应用 | highscalability.com | | 设计一个类似 Instagram 的图片分享系统 | highscalability.com
highscalability.com | | 设计 Facebook 新闻推送功能 | quora.com
quora.com
slideshare.net | | 设计 Facebook 时间线功能 | facebook.com
highscalability.com | | 设计 Facebook 聊天功能 | erlang-factory.com
facebook.com |

| 设计一个类似 Facebook 的图搜索功能 | facebook.com
facebook.com
facebook.com | | 设计一个类似 CloudFlare 的内容分发网络 | figshare.com | | 设计一个类似 Twitter 的热门话题系统 | michael-noll.com
snikolov .wordpress.com | | 设计一个随机 ID 生成系统 | blog.twitter.com
github.com | | 返回某一时间区间内的前 k 个请求 | cs.ucsb.edu
wpi.edu | | 设计一个从多个数据中心提供数据的系统 | highscalability.com | | 设计一个在线多人纸牌游戏 | indieflashblog.com
buildnewgames.com | | 设计一个垃圾回收系统 | stuffwithstuff.com
washington.edu | | 设计一个 API 速率限制器 | https://stripe.com/blog/ | | 设计一个股票交易所(如 NASDAQ 或 Binance) | Jane Street
Golang 实现
Go 实现 | | 添加一个系统设计问题 | 贡献 |

现实世界架构

关于现实世界系统设计的文章。


来源:Twitter 时间线的可扩展性

以下文章不要关注细枝末节,而应:

| 类型 | 系统 | 参考资料 | |---|---|---| | 数据处理 | MapReduce - Google 的分布式数据处理 | research.google.com | | 数据处理 | Spark - Databricks 的分布式数据处理 | slideshare.net | | 数据处理 | Storm - Twitter 的分布式数据处理 | slideshare.net | | | | | | 数据存储 | Bigtable - Google 的分布式列式数据库 | harvard.edu | | 数据存储 | HBase - Bigtable 的开源实现 | slideshare.net | | 数据存储 | Cassandra - Facebook 的分布式列式数据库 | slideshare.net | | 数据存储 | DynamoDB - Amazon 的文档数据库 | harvard.edu | | 数据存储 | MongoDB - 文档数据库 | slideshare.net | | 数据存储 | Spanner - Google 的全球分布式数据库 | research.google.com | | 数据存储 | Memcached - 分布式内存缓存系统 | slideshare.net | | 数据存储 | Redis - 具有持久化和多种值类型的分布式内存缓存系统 | slideshare.net | | | | | | 文件系统 | 谷歌文件系统 (GFS) - 分布式文件系统 | research.google.com | | 文件系统 | Hadoop 文件系统 (HDFS) - GFS 的开源实现 | apache.org | | | | | | 其他 | Chubby - 谷歌为松耦合分布式系统提供的锁服务 | research.google.com | | 其他 | Dapper - 分布式系统追踪基础设施 | research.google.com | 其他 | Kafka - 来自 LinkedIn 的发布/订阅消息队列 | slideshare.net | | 其他 | Zookeeper - 实现同步的集中式基础设施和服务 | slideshare.net | | | 添加一个架构 | 贡献 |

公司架构

| 公司 | 参考资料 | |---|---| | 亚马逊 | 亚马逊架构 | | Cinchcast | 每天生产1500小时音频 | | DataSift | 每秒12万条推文的实时数据挖掘 | | Dropbox | 我们如何扩展 Dropbox | | ESPN | 每秒10万“呃呃呃”操作的运行 | | 谷歌 | 谷歌架构 | | Instagram | 1400万用户,数TB照片
Instagram的动力 | | Justin.tv | Justin.Tv 的直播视频广播架构 | | Facebook | Facebook 上的 memcached 扩展
TAO:Facebook 社交图的分布式数据存储
Facebook 的照片存储
Facebook 实时直播给80万同时观看者 | | Flickr | Flickr 架构 | | Mailbox | 6周内从0到100万用户 | | Netflix | Netflix 全栈360度视图
Netflix:按下播放键会发生什么? | | Pinterest | 从0到数百亿月页面浏览量
1800万访客,10倍增长,12名员工 | | Playfish | 5000万月活用户且持续增长 | | PlentyOfFish | PlentyOfFish 架构 | | Salesforce | 每天处理13亿交易 | | Stack Overflow | Stack Overflow 架构 | | TripAdvisor | 4000万访客,2亿动态页面浏览,30TB数据 | | Tumblr | 每月150亿页面浏览量 | | Twitter | 让 Twitter 快1万倍
使用 MySQL 存储每天2.5亿条推文
1.5亿活跃用户,30万QPS,22MB/s数据流
大规模时间线
Twitter 的大数据与小数据
Twitter 运维:超越1亿用户的扩展
Twitter 如何处理每秒3000张图片 | | Uber | Uber 如何扩展其实时市场平台
扩展 Uber 到2000名工程师、1000个服务和8000个 Git 仓库的经验教训 | | WhatsApp | Facebook 以190亿美元收购的 WhatsApp 架构 | | YouTube | YouTube 可扩展性
YouTube 架构 |

公司工程博客

你面试的公司的架构。
>
你遇到的问题可能来自同一领域。

#### 来源及进一步阅读

想添加博客?为了避免重复劳动,考虑将贵公司的博客添加到以下仓库:

开发中

有兴趣添加新章节或帮助完成正在进行中的章节?贡献

致谢

致谢和来源贯穿本仓库。

特别感谢:

联系方式

欢迎随时联系我讨论任何问题、疑问或意见。

我的联系方式可以在我的GitHub 页面找到。

许可证

我在此仓库中向您提供的代码和资源均遵循开源许可证。由于这是我的个人仓库,您获得的代码和资源的许可证由我本人而非我的雇主(Facebook)授权。

版权 2017 Donne Martin

知识共享 署名 4.0 国际许可协议 (CC BY 4.0)

http://creativecommons.org/licenses/by/4.0/

--- Tranlated By Open Ai Tx | Last indexed: 2025-08-09 ---