English ∙ 日本語 ∙ 简体中文 ∙ 繁體中文 |العَرَبِيَّة ∙ বাংলা ∙ Português do Brasil ∙ Deutsch ∙ ελληνικά ∙ עברית ∙ Italiano ∙ 한국어 ∙ فارسی ∙ Polski ∙ русский язык ∙ Español ∙ ภาษาไทย ∙ Türkçe ∙ tiếng Việt ∙ Français |添加翻译
帮助翻译本指南!
了解如何设计大型系统。
准备系统设计面试。
学习如何设计可扩展的系统将帮助你成为一名更好的工程师。
系统设计是一个广泛的话题。关于系统设计原则,网络上散布着大量资源。
此存储库是有组织的资源集合,可帮助你了解如何大规模构建系统。
这是一个不断更新的开源项目。
欢迎投稿!
除了编码面试外,系统设计是许多科技公司技术面试过程的必要组成部分。
练习常见的系统设计面试问题,并将结果与示例解决方案(讨论、代码和图表)进行比较。
面试准备的其他主题:
提供的 Anki 抽认卡组使用间隔重复来帮助你保留关键的系统设计概念。
非常适合在旅途中使用。
正在寻找资源来帮助你准备编码面试?
查看姊妹存储库 Interactive Coding Challenges,其中包含一个额外的 Anki 套牌:
向社区学习。
请随时提交拉取请求以帮助:
需要一些润色的内容正在开发中。
查看贡献指南。
各种系统设计主题的摘要,包括优点和缺点。 一切都是权衡取舍。
每个部分都包含指向更深入资源的链接。
根据你的面试时间表(短、中、长)建议要审查的主题。
问:对于面试,我需要知道这里的一切吗?
答:不,你不需要知道这里的一切来准备面试。
面试中会问到什么取决于以下变量:
更有经验的考生通常应该更多地了解系统设计。架构师或团队领导可能比个人贡献者知道的更多。顶级科技公司可能会进行一轮或多轮设计面试。
从广义开始,在几个领域深入。它有助于了解一些关于各种关键系统设计主题的知识。根据你的时间表、经验、你正在面试的职位以及你正在面试的公司调整以下指南。
短 | 中等 | 长 | |
---|---|---|---|
通读系统设计主题,以广泛了解系统的工作原理 | 👍 | 👍 | 👍 |
通读公司工程博客中的几篇文章,了解你正在面试的公司 | 👍 | 👍 | 👍 |
通读一些真实世界的架构 | 👍 | 👍 | 👍 |
复习如何处理系统设计面试问题 | 👍 | 👍 | 👍 |
通过系统设计面试问题和解决方案进行工作 | 一些 | 多 | 最 |
解决面向对象的设计面试问题和解决方案 | 一些 | 多 | 最 |
查看其他系统设计面试问题 | 一些 | 多 | 最 |
如何解决系统设计面试问题。
系统设计面试是一个开放式的对话。你应该领导它。
你可以使用以下步骤来指导讨论。为了帮助巩固此过程,请使用以下步骤完成系统设计面试问题和解决方案部分。
收集需求并确定问题范围。提出问题以阐明用例和约束。讨论假设。
概述包含所有重要组件的高级设计。
深入了解每个核心组件的详细信息。例如,如果你被要求设计一个 url 缩短服务,请讨论:
在给定约束的情况下,识别并解决瓶颈。例如,是否需要以下内容来解决可伸缩性问题?
讨论潜在的解决方案和权衡。一切都是权衡取舍。使用可扩展系统设计原则解决瓶颈问题。
你可能会被要求手动进行一些估算。有关以下资源,请参阅附录:
请查看以下链接,以更好地了解预期内容:
常见的系统设计面试问题,包括示例讨论、代码和图表。
链接到文件夹中内容的解决方案。
solutions/
问题 | |
---|---|
设计 Pastebin.com(或 Bit.ly) | 溶液 |
设计 Twitter 时间线和搜索(或 Facebook 提要和搜索) | 溶液 |
设计网络爬虫 | 溶液 |
设计 Mint.com | 溶液 |
设计社交网络的数据结构 | 溶液 |
为搜索引擎设计键值存储 | 溶液 |
按类别功能设计亚马逊的销售排名 | 溶液 |
在 AWS 上设计可扩展到数百万用户的系统 | 溶液 |
添加系统设计问题 | 贡献 |
常见的面向对象设计面试问题,包括示例讨论、代码和图表。
链接到文件夹中内容的解决方案。
solutions/
注意:此部分正在开发中
问题 | |
---|---|
设计哈希映射 | 溶液 |
设计最近最少使用的缓存 | 溶液 |
设计呼叫中心 | 溶液 |
设计一副纸牌 | 溶液 |
设计停车场 | 溶液 |
设计聊天服务器 | 溶液 |
设计圆形阵列 | 贡献 |
添加面向对象的设计问题 | 贡献 |
刚接触系统设计?
首先,你需要对常见原则有一个基本的了解,了解它们是什么、如何使用它们以及它们的优缺点。
接下来,我们将了解高级权衡:
请记住,一切都是权衡取舍。
然后,我们将深入探讨更具体的主题,例如 DNS、CDN 和负载均衡器。
如果服务以与添加的资源成正比的方式提高性能,则该服务是可伸缩的。通常,提高性能意味着为更多的工作单元提供服务,但也可以是处理更大的工作单元,例如当数据集增长时。1
查看性能与可伸缩性的另一种方法:
延迟是执行某些操作或产生某些结果的时间。
吞吐量是每单位时间内此类操作或结果的数量。
通常,应以可接受的延迟实现最大吞吐量为目标。
在分布式计算机系统中,只能支持以下两项保证:
网络不可靠,因此需要支持分区容错。你需要在一致性和可用性之间进行软件权衡。
等待分区节点的响应可能会导致超时错误。如果你的业务需求需要原子读取和写入,CP 是一个不错的选择。
响应返回任何节点上可用数据的最容易获得的版本,这可能不是最新的。解析分区时,写入可能需要一些时间才能传播。
如果业务需要允许最终一致性,或者当系统需要在外部错误的情况下继续工作时,AP 是一个不错的选择。
对于同一数据的多个副本,我们面临着如何同步它们的选项,以便客户对数据有一个一致的视图。回想一下 CAP 定理中一致性的定义 - 每次读取都会收到最近的写入或错误。
写入后,读取者可能会或可能不会看到它。采取尽力而为的方法。
这种方法在 memcached 等系统中可见。弱一致性在实时用例(如 VoIP、视频聊天和实时多人游戏)中效果很好。例如,如果你在通话中失去接收几秒钟,当你重新获得连接时,你听不到连接中断期间所说的内容。
写入后,读取最终会看到它(通常在几毫秒内)。数据是异步复制的。
这种方法在 DNS 和电子邮件等系统中可见。最终一致性在高可用性系统中效果很好。
写入后,读取者将看到它。数据是同步复制的。
这种方法在文件系统和 RDBMS 中可见。强一致性在需要事务的系统中效果很好。
有两种互补模式可以支持高可用性:故障转移和复制。
通过主动-被动故障转移,检测信号在备用服务器和无源服务器之间发送。如果检测信号中断,无源服务器将接管主用服务器的 IP 地址并恢复服务。
停机时间的长短取决于无源服务器是否已经在“热”备用状态下运行,或者是否需要从“冷”备用状态启动。只有活动服务器处理流量。
主动-被动故障切换也称为主-从故障切换。
在主动-主动模式下,两台服务器都在管理流量,在它们之间分散负载。
如果服务器是面向公众的,则 DNS 需要了解两台服务器的公共 IP。如果服务器面向内部,则应用程序逻辑需要了解这两台服务器。
主动-主动故障切换也称为主-主故障切换。
本主题将在“数据库”部分中进一步讨论:
可用性通常通过正常运行时间(或停机时间)来量化,作为服务可用时间的百分比。可用性通常以 9 的数量来衡量,可用性为 99.99% 的服务被描述为具有四个 9。
期间 | 可接受的停机时间 |
---|---|
每年停机时间 | 8小时 45分 57秒 |
每月停机时间 | 43 分 49.7 秒 |
每周停机时间 | 10 分 4.8 秒 |
每天停机时间 | 1 分 26.4 秒 |
期间 | 可接受的停机时间 |
---|---|
每年停机时间 | 52分 35.7秒 |
每月停机时间 | 4 分 23 秒 |
每周停机时间 | 1 分 5 秒 |
每天停机时间 | 8.6秒 |
如果服务由多个容易发生故障的组件组成,则服务的整体可用性取决于这些组件是按顺序还是并行。
当两个可用性< 100% 的组件依次排列时,总体可用性会降低:
Availability (Total) = Availability (Foo) * Availability (Bar)
如果两者和每个都有 99.9% 的可用性,则它们的总可用性依次为 99.8%。
Foo
Bar
当两个可用性< 100% 的组件并行时,总体可用性会提高:
Availability (Total) = 1 - (1 - Availability (Foo)) * (1 - Availability (Bar))
如果两者和每个都有 99.9% 的可用性,则它们的并行总可用性将为 99.9999%。
Foo
Bar
域名系统 (DNS) 将域名(如 www.example.com)转换为 IP 地址。
DNS 是分层的,在顶层有一些权威服务器。你的路由器或 ISP 提供有关在执行查找时要联系的 DNS 服务器的信息。较低级别的 DNS 服务器缓存映射,这些映射可能会因 DNS 传播延迟而过时。你的浏览器或操作系统也可以将 DNS 结果缓存一段时间,由生存时间 (TTL) 决定。
CNAME
A
CloudFlare 和 Route 53 等服务提供托管 DNS 服务。某些 DNS 服务可以通过各种方法路由流量:
内容分发网络 (CDN) 是一个全球分布的代理服务器网络,从离用户更近的位置提供内容。通常,HTML/CSS/JS、照片和视频等静态文件由 CDN 提供,尽管某些 CDN(如 Amazon 的 CloudFront)支持动态内容。站点的 DNS 解析将告诉客户端要联系哪个服务器。
从 CDN 提供内容可以通过两种方式显著提高性能:
每当服务器上发生更改时,推送 CDN 都会接收新内容。你全权负责提供内容、直接上传到 CDN 以及重写 URL 以指向 CDN。你可以配置内容何时过期以及何时更新内容。仅当内容是新的或更改的内容时才上传,从而最大限度地减少流量,但最大化存储空间。
流量较小的网站或内容不经常更新的网站可以很好地与推送 CDN 配合使用。 内容在 CDN 上放置一次,而不是定期重新拉取。
拉取 CDN 在第一个用户请求内容时从你的服务器获取新内容。将内容保留在服务器上,并重写 URL 以指向 CDN。这会导致请求速度变慢,直到内容缓存到 CDN 上。
生存时间 (TTL) 决定了缓存内容的时间。拉取 CDN 可最大程度地减少 CDN 上的存储空间,但如果文件过期并在实际更改之前拉取,则可能会产生冗余流量。
流量大的网站与拉式 CDN 配合得很好,因为流量分布得更均匀,CDN 上只保留最近请求的内容。
负载均衡器将传入的客户端请求分发到计算资源,例如应用程序服务器和数据库。在每种情况下,负载均衡器都会将响应从计算资源返回到相应的客户端。负载均衡器在以下方面有效:
负载均衡器可以使用硬件(昂贵)或软件(如 HAProxy)来实现。
其他好处包括:
为了防止故障,通常在主动-被动或主动-主动模式下设置多个负载均衡器。
负载均衡器可以根据各种指标路由流量,包括:
第 4 层负载均衡器查看传输层的信息,以决定如何分发请求。通常,这涉及标头中的源、目标 IP 地址和端口,但不涉及数据包的内容。第 4 层负载均衡器将网络数据包转发到上游服务器或从上游服务器转发网络数据包,执行网络地址转换 (NAT)。
第 7 层负载均衡器查看应用程序层以决定如何分发请求。这可能涉及标头、消息和 Cookie 的内容。第 7 层负载平衡器终止网络流量,读取消息,做出负载平衡决策,然后打开与所选服务器的连接。例如,第 7 层负载均衡器可以将视频流量定向到托管视频的服务器,同时将更敏感的用户计费流量定向到安全强化的服务器。
以灵活性为代价,第 4 层负载均衡比第 7 层需要更少的时间和计算资源,尽管对现代商用硬件的性能影响可能很小。
负载均衡器还可以帮助进行水平扩展,从而提高性能和可用性。与在更昂贵的硬件上纵向扩展单个服务器(称为垂直缩放)相比,使用商用计算机进行横向扩展更具成本效益,并且可以带来更高的可用性。与专业企业系统相比,雇用从事商用硬件工作的人才也更容易。
反向代理是一种 Web 服务器,它集中了内部服务并向公众提供统一的接口。在反向代理将服务器的响应返回给客户端之前,来自客户端的请求将转发到可以完成该请求的服务器。
其他好处包括:
来源:Intro to Architecting Systems for Scale
通过将 Web 层与应用层(也称为平台层)分离出来,你可以独立扩展和配置这两个层。添加新的 API 会导致添加应用程序服务器,而不必添加其他 Web 服务器。单一责任原则提倡协同工作的小型和自主服务。拥有小型服务的小型团队可以更积极地规划快速增长。
应用层的工作线程还有助于实现异步。
与此讨论相关的是微服务,它可以描述为一套可独立部署的小型模块化服务。每个服务都运行一个独特的流程,并通过定义明确的轻量级机制进行通信,以实现业务目标。1
例如,Pinterest 可以有以下微服务:用户个人资料、关注者、提要、搜索、照片上传等。
Consul、Etcd 和 Zookeeper 等系统可以通过跟踪注册名称、地址和端口来帮助服务找到彼此。运行状况检查有助于验证服务完整性,通常使用 HTTP 终结点完成。Consul 和 Etcd 都有一个内置的键值存储,可用于存储配置值和其他共享数据。
像 SQL 这样的关系数据库是以表形式组织的数据项的集合。
ACID 是关系数据库事务的一组属性。
有许多技术可以扩展关系数据库:主从复制、主-主复制、联合、分片、非规范化和 SQL 优化。
主服务器提供读取和写入,将写入复制到一个或多个仅提供读取的从服务器。从属服务器还可以以树状的方式复制到其他从属服务器。如果主设备脱机,系统可以继续以只读模式运行,直到从设备升级为主设备或配置新的主设备。
两个主服务器都提供读取和写入,并在写入时相互协调。如果任一主设备出现故障,系统可以继续进行读取和写入操作。
联合(或功能分区)按功能拆分数据库。例如,你可以拥有三个数据库:论坛、用户和产品,而不是单个整体式数据库,从而减少每个数据库的读取和写入流量,从而减少复制滞后。数据库越小,内存中可以容纳的数据就越多,这反过来又会由于缓存局部性的改进而导致更多的缓存命中。由于没有单个中央主序列化写入,因此你可以并行写入,从而提高吞吐量。
分片将数据分布在不同的数据库之间,因此每个数据库只能管理数据的子集。以用户数据库为例,随着用户数量的增加,集群中加入的分片也越来越多。
与联合的优点类似,分片可以减少读取和写入流量、减少复制和增加缓存命中。索引大小也会减小,这通常会通过更快的查询来提高性能。如果一个分片出现故障,其他分片仍可运行,但你需要添加某种形式的复制以避免数据丢失。与联合一样,没有单个中央主序列化写入,允许你并行写入,同时提高吞吐量。
对用户表进行分片的常用方法是通过用户的姓氏、首字母或用户的地理位置。
非规范化想以牺牲某些写入性能为代价来提高读取性能。数据的冗余副本写入多个表中,以避免成本高昂的联接。一些 RDBMS(如 PostgreSQL 和 Oracle)支持物化视图,这些视图处理存储冗余信息和保持冗余副本一致性的工作。
一旦数据通过联合和分片等技术进行分布式处理,跨数据中心管理联接将进一步增加复杂性。非规范化可能会规避对这种复杂连接的需求。
在大多数系统中,读取次数可能远远超过写入次数 100:1 甚至 1000:1。导致复杂数据库联接的读取可能非常昂贵,在磁盘操作上花费大量时间。
SQL 调优是一个广泛的主题,已经编写了许多书籍作为参考。
对基准测试和分析进行建模以模拟和发现瓶颈非常重要。
基准测试和分析可能会指向以下优化。
CHAR
VARCHAR
CHAR有效地允许快速、随机的访问,而使用 ,你必须先找到一个字符串的末尾,然后才能进入下一个字符串。
VARCHAR
TEXT
TEXT
TEXT
INT
DECIMAL
BLOBS
VARCHAR(255)是 8 位数字中可以计数的最大字符数,通常在某些 RDBMS 中最大化使用一个字节。
NOT NULL
SELECT
GROUP BY
ORDER BY
JOIN
NoSQL 是以键值存储、文档存储、宽列存储或图形数据库表示的数据项的集合。数据是非规范化的,联接通常在应用程序代码中完成。大多数 NoSQL 存储缺乏真正的 ACID 事务,并且有利于最终一致性。
BASE 通常用于描述 NoSQL 数据库的属性。与 CAP 定理相比,BASE 选择可用性而不是一致性。
除了在 SQL 或 NoSQL 之间进行选择之外,了解哪种类型的 NoSQL 数据库最适合你的用例也很有帮助。我们将在下一节中介绍键值存储、文档存储、宽列存储和图形数据库。
抽象:哈希表
键值存储通常允许 O(1) 读取和写入,并且通常由内存或 SSD 提供支持。数据存储可以按字典顺序维护密钥,从而可以有效地检索密钥范围。键值存储可以允许存储具有值的元数据。
键值存储提供高性能,通常用于简单的数据模型或快速变化的数据,例如内存中缓存层。由于它们仅提供一组有限的操作,因此如果需要其他操作,复杂性会转移到应用层。
键值存储是更复杂系统(如文档存储)的基础,在某些情况下,还有图形数据库。
抽象:键值存储,文档存储为值
文档存储以文档(XML、JSON、二进制等)为中心,其中文档存储给定对象的所有信息。文档存储提供 API 或查询语言,用于根据文档本身的内部结构进行查询。请注意,许多键值存储都包含用于处理值元数据的功能,从而模糊了这两种存储类型之间的界限。
根据基础实现,文档按集合、标记、元数据或目录进行组织。尽管文档可以组织或组合在一起,但文档可能具有彼此完全不同的字段。
一些文档存储(如 MongoDB 和 CouchDB)也提供类似 SQL 的语言来执行复杂的查询。DynamoDB 支持键值对和文档。
文档存储提供了高度的灵活性,通常用于处理偶尔更改的数据。
抽象:嵌套映射
ColumnFamily<RowKey, Columns<ColKey, Value, Timestamp>>
宽列存储的基本数据单位是列(名称/值对)。列可以按列族进行分组(类似于 SQL 表)。超级色谱柱族进一步对色谱柱族进行分组。你可以使用行键独立访问每一列,具有相同行键的列构成一行。每个值都包含用于版本控制和冲突解决的时间戳。
Google引入了Bigtable作为第一个宽列存储,这影响了Hadoop生态系统中经常使用的开源HBase,以及Facebook的Cassandra。BigTable、HBase 和 Cassandra 等存储按字典顺序维护密钥,从而可以高效检索选择性密钥范围。
宽列存储提供高可用性和高可伸缩性。它们通常用于非常大的数据集。
抽象:图形
在图形数据库中,每个节点都是一条记录,每个弧是两个节点之间的关系。图形数据库经过优化,可以表示具有许多外键或多对多关系的复杂关系。
图形数据库为具有复杂关系的数据模型(例如社交网络)提供高性能。它们相对较新,尚未广泛使用;查找开发工具和资源可能更加困难。许多图形只能使用 REST API 访问。
使用 SQL 的原因:
使用 NoSQL 的原因:
非常适合 NoSQL 的示例数据:
缓存可缩短页面加载时间,并可减少服务器和数据库的负载。在此模型中,调度程序将首先查找之前是否发出过请求,并尝试查找要返回的先前结果,以保存实际执行。
数据库通常受益于跨分区的读取和写入的均匀分布。热门项目可能会扭曲分布,从而导致瓶颈。将缓存放在数据库的前面有助于吸收不均匀的负载和流量峰值。
缓存可以位于客户端(操作系统或浏览器)、服务器端或不同的缓存层中。
CDN 被视为一种缓存。
反向代理和缓存(如 Varnish)可以直接提供静态和动态内容。Web 服务器还可以缓存请求,返回响应,而无需联系应用程序服务器。
你的数据库通常在默认配置中包含某种级别的缓存,并针对通用用例进行了优化。针对特定使用模式调整这些设置可以进一步提高性能。
Memcached 和 Redis 等内存缓存是应用程序和数据存储之间的键值存储。由于数据保存在 RAM 中,因此它比数据存储在磁盘上的典型数据库快得多。RAM 比磁盘更受限制,因此缓存失效算法(如最近最少使用过 (LRU))可以帮助使“冷”条目失效,并将“热”数据保留在 RAM 中。
Redis 具有以下附加功能:
可以缓存多个级别,分为两个常规类别:数据库查询和对象:
通常,应尽量避免基于文件的缓存,因为它会使克隆和自动缩放更加困难。
每当查询数据库时,请将查询作为键进行哈希处理,并将结果存储到缓存中。此方法存在过期问题:
将数据视为对象,类似于对应用程序代码执行的操作。让应用程序将数据库中的数据集组合到类实例或数据结构中:
缓存内容的建议:
由于你只能在缓存中存储有限数量的数据,因此你需要确定哪种缓存更新策略最适合你的用例。
该应用程序负责从存储中读取和写入。缓存不直接与存储交互。该应用程序执行以下操作:
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 通常以这种方式使用。
对添加到缓存中的数据的后续读取速度很快。缓存端也称为延迟加载。仅缓存请求的数据,从而避免使用未请求的数据填满缓存。
应用程序使用缓存作为主数据存储,向其读取和写入数据,而缓存负责读取和写入数据库:
应用代码:
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)
由于写入操作,直写是一个缓慢的整体操作,但后续读取仅写入的数据很快。用户在更新数据时通常比读取数据时更能容忍延迟。缓存中的数据不会过时。
在后写中,应用程序执行以下操作:
你可以将缓存配置为在过期之前自动刷新任何最近访问的缓存条目。
如果缓存可以准确预测将来可能需要哪些项目,则提前刷新可以减少延迟而不是读取。
来源:Intro to Architecting Systems for Scale
异步工作流有助于减少成本高昂的操作的请求时间,否则这些操作将在线执行。他们还可以通过提前完成耗时的工作来提供帮助,例如定期聚合数据。
消息队列接收、保留和传递消息。如果操作速度太慢而无法以内联方式执行,则可以将消息队列与以下工作流结合使用:
用户不会被阻止,作业将在后台处理。在此期间,客户端可以选择执行少量处理,使其看起来像任务已完成。例如,如果发布一条推文,该推文可能会立即发布到你的时间线,但你的推文可能需要一些时间才能真正发送给你的所有关注者。
Redis 作为简单的消息代理很有用,但消息可能会丢失。
RabbitMQ 很受欢迎,但需要你适应“AMQP”协议并管理自己的节点。
Amazon SQS 是托管的,但延迟可能很高,并且消息可能会传输两次。
任务队列接收任务及其相关数据,运行它们,然后交付其结果。它们可以支持调度,并可用于在后台运行计算密集型作业。
Celery 支持调度,主要支持 python。
如果队列开始显著增长,则队列大小可能会大于内存,从而导致缓存未命中、磁盘读取,甚至性能降低。背压可以通过限制队列大小来提供帮助,从而为队列中已有的作业保持高吞吐率和良好的响应时间。队列填满后,客户端会收到服务器繁忙或 HTTP 503 状态代码,以便稍后重试。客户端可以在以后重试请求,可能会使用指数退避。
HTTP 是一种在客户端和服务器之间编码和传输数据的方法。它是一种请求/响应协议:客户端发出请求,服务器发出响应,其中包含有关请求的相关内容和完成状态信息。HTTP 是独立的,允许请求和响应流经许多执行负载平衡、缓存、加密和压缩的中间路由器和服务器。
基本 HTTP 请求由动词(方法)和资源(终结点)组成。以下是常见的 HTTP 谓词:
动词 | 描述 | 幂等* | 安全 | 可缓存 |
---|---|---|---|---|
获取 | 读取资源 | 是的 | 是的 | 是的 |
发布 | 创建资源或触发处理数据的进程 | 不 | 不 | 如果响应包含新鲜度信息,则为“是” |
放 | 创建或替换资源 | 是的 | 不 | 不 |
补丁 | 部分更新资源 | 不 | 不 | 如果响应包含新鲜度信息,则为“是” |
删除 | 删除资源 | 是的 | 不 | 不 |
*可以多次调用,结果不尽相同。
HTTP是一种应用层协议,依赖于TCP和UDP等较低级别的协议。
TCP 是 IP 网络上面向连接的协议。使用握手建立和终止连接。通过以下方式保证发送的所有数据包都按原始顺序到达目的地,并且不会损坏:
如果发送方未收到正确的响应,它将重新发送数据包。如果有多个超时,则连接将断开。TCP 还实现了流量控制和拥塞控制。这些保证会导致延迟,并且通常会导致传输效率低于 UDP。
为了确保高吞吐量,Web 服务器可能会使大量 TCP 连接保持打开状态,从而导致内存使用率过高。在 Web 服务器线程和 memcached 服务器之间拥有大量开放连接可能会很昂贵。除了在适用的情况下切换到 UDP 之外,连接池也会有所帮助。
TCP 对于需要高可靠性但对时间要求不高的应用程序非常有用。一些示例包括 Web 服务器、数据库信息、SMTP、FTP 和 SSH。
在以下情况下使用 TCP over UDP:
UDP 是无连接的。数据报(类似于数据包)仅在数据报级别得到保证。数据报可能会无序到达目的地或根本无法到达目的地。UDP 不支持拥塞控制。如果没有TCP支持的保证,UDP通常更有效。
UDP 可以广播,将数据报发送到子网上的所有设备。这对于 DHCP 很有用,因为客户端尚未收到 IP 地址,从而阻止了 TCP 在没有 IP 地址的情况下进行流式传输的方式。
UDP 不太可靠,但在 VoIP、视频聊天、流媒体和实时多人游戏等实时用例中效果很好。
在以下情况下使用 UDP over TCP:
在 RPC 中,客户端会导致过程在不同的地址空间(通常是远程服务器)上执行。该过程被编码为本地过程调用,从客户端程序中抽象出如何与服务器通信的细节。远程调用通常比本地调用更慢且更不可靠,因此区分 RPC 调用和本地调用很有帮助。流行的 RPC 框架包括 Protobuf、Thrift 和 Avro。
RPC 是一种请求-响应协议:
RPC 调用示例:
GET /someoperation?data=anId POST /anotheroperation { "data":"anId"; "anotherdata": "another value" }
RPC 侧重于公开行为。RPC 通常用于内部通信的性能原因,因为你可以手动制作本机调用以更好地适应你的用例。
在以下情况下选择本机库(又名 SDK):
遵循 REST 的 HTTP API 往往更频繁地用于公共 API。
REST是一种架构风格,它强制执行客户端/服务器模型,其中客户端作用于服务器管理的一组资源。服务器提供资源和操作的表示形式,这些资源和操作可以操作或获取资源的新表示形式。所有通信都必须是无状态且可缓存的。
RESTful 接口有四种特性:
REST 调用示例:
GET /someresources/anId PUT /someresources/anId {"anotherdata": "another value"}
REST专注于公开数据。它最大限度地减少了客户端/服务器之间的耦合,通常用于公共 HTTP API。REST 使用一种更通用和统一的方法,通过 URI 公开资源,通过标头表示资源,并通过 GET、POST、PUT、DELETE 和 PATCH 等动词执行操作。由于是无状态的,REST非常适合水平扩展和分区。
操作 | RPC格式 | 休息 |
---|---|---|
注册 | 开机自检/注册 | POST /人 |
辞职 |
POST /resign { “personid”: “1234” } |
删除 /persons/1234 |
阅读一个人 | 获取 /readPerson?personid=1234 | 获取 /persons/1234 |
读取人员的项目列表 | 获取 /readUsersItemsList?personid=1234 | GET /persons/1234/items |
将项目添加到人员的项目 |
POST /addItemToUsersItemsList { “personid”: “1234”; ”itemid“: ”456” } |
发布 /persons/1234/items { “itemid”: “456” } |
更新项目 |
POST /modifyItem { “itemid”: “456”; ”key“: ”值” } |
PUT /items/456 { “key”: “值” } |
删除项目 |
POST /removeItem { “itemid”: “456” } |
删除 /items/456 |
来源:你真的知道为什么你更喜欢 REST 而不是 RPC 吗
本部分可能会使用一些更新。考虑贡献!
安全性是一个广泛的话题。除非你有丰富的经验、安全背景,或者正在申请需要安全知识的职位,否则你可能只需要了解基础知识:
你有时会被要求进行“粗略”估算。例如,你可能需要确定从磁盘生成 100 个图像缩略图所需的时间,或者数据结构将占用多少内存。每个程序员都应该知道的 Powers of two table 和 Latency 数字是方便的参考。
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 |
设计一个像谷歌这样的搜索引擎 |
queue.acm.org stackexchange.com ardendertat.com stanford.edu |
设计一个可扩展的网络爬虫,如谷歌 | quora.com |
设计 Google 文档 |
code.google.com neil.fraser.name |
设计一个像 Redis 这样的键值存储 | slideshare.net |
设计一个像 Memcached 这样的缓存系统 | slideshare.net |
设计一个像亚马逊一样的推荐系统 |
hulu.com ijcai13.org |
设计一个像 Bitly 这样的 tinyurl 系统 | 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/ |
设计证券交易所(如纳斯达克或币安) |
Jane Street Golang 实现 Go 实现 |
添加系统设计问题 | 贡献 |
有关如何设计真实世界系统的文章。
不要关注以下文章的细节,而是:
类型 | 系统 | 参考资料 |
---|---|---|
数据处理 | 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 |
文件系统 | Google 文件系统 (GFS) - 分布式文件系统 | research.google.com |
文件系统 | Hadoop 文件系统 (HDFS) - GFS 的开源实现 | apache.org |
杂项 | Chubby - Google 为松散耦合的分布式系统提供锁定服务 | research.google.com |
杂项 | Dapper - 分布式系统跟踪基础结构 | research.google.com |
杂项 | Kafka - 来自 LinkedIn 的发布/订阅消息队列 | slideshare.net |
杂项 | Zookeeper - 支持同步的集中式基础架构和服务 | slideshare.net |
添加体系结构 | 贡献 |
公司 | 参考资料 |
---|---|
亚马逊河 | Amazon 架构 |
辛奇卡斯特 | 每天制作 1,500 小时的音频 |
数据筛 | 实时数据挖掘 每秒 120,000 条推文 |
Dropbox的 | 我们如何扩展 Dropbox |
ESPN电视台 | 以每秒 100,000 duh nuh nuhs 的速度运行 |
谷歌 | Google 架构 |
Instagram的 |
1400 万用户,数 TB 的照片 是什么为 Instagram 提供动力 |
Justin.tv | Justin.Tv 的视频直播架构 |
脸书 |
在 Facebook 上扩展 memcachedTAO:Facebook 用于社交图 谱的分布式数据存储 Facebook 的照片存储 Facebook 如何向 800,000 名同时观看者直播 |
Flickr的 | Flickr 架构 |
邮箱 | 在 6 周内从 0 到 100 万用户 |
Netflix公司 |
整个 Netflix 堆栈 的 360 度视图 Netflix:按下播放键时会发生什么? |
Pinterest的 |
每月 从0到10数十亿页面浏览量,1800万访问者,10倍增长,12名员工 |
戏鱼 | 每月 5000 万用户,并且还在不断增长 |
PlentyOfFish(丰盛的鱼) | PlentyOfFish 架构 |
Salesforce的 | 他们每天如何处理 13 亿笔交易 |
堆栈溢出 | Stack Overflow 架构 |
猫途鹰 | 40M访客,200M动态页面浏览量,30TB数据 |
Tumblr的 | 每月 150 亿次页面浏览量 |
唽 |
使 Twitter 速度提高 10000% 使用 MySQL 每天存储 2.5 亿条推文 150M 活跃用户,300K QPS,22 MB/S 消防水带 大规模 时间线 Twitter 的大小数据 Twitter 运营:用户数超过 1 亿 Twitter 如何每秒处理 3,000 张图像 |
优步 |
Uber 如何扩展其实时市场平台 将 Uber 扩展到 2000 名工程师、1000 项服务和 8000 个 Git 存储库的经验教训 |
WhatsApp的 | Facebook 以 190 亿美元收购的 WhatsApp 架构 |
优酷 |
YouTube 可扩展性 YouTube 架构 |
你正在面试的公司的架构。
你遇到的问题可能来自同一域。
想要添加博客?为避免重复工作,请考虑将公司博客添加到以下存储库:
有兴趣添加一个部分或帮助完成一个正在进行的部分吗?贡献!
此存储库中提供了信用和源。
特别鸣谢:
请随时与我联系以讨论任何问题、疑问或意见。
我的联系信息可以在我的 GitHub 页面上找到。
我在此存储库中根据开源许可向你提供代码和资源。因为这是我的个人存储库,所以你收到的我的代码和资源的许可证来自我,而不是我的雇主 (Facebook)。
Copyright 2017 Donne Martin Creative Commons Attribution 4.0 International License (CC BY 4.0) http://creativecommons.org/licenses/by/4.0/