简体中文 ∙ 日本語 ∙ 简体中文 ∙ 繁體中文 |العَرَبِيَّة ∙ বাংলা ∙ 葡萄牙做巴西 ∙ 德语 ∙ ελληνικά ∙ עברית ∙ Italiano ∙ 한국어 ∙ فارسی ∙ 波兰语 ∙ русский язык ∙ 西班牙语 ∙ ภาษาไทย ∙ 土耳其 ∙ 越南 ∙ 法语 |添加翻译
帮助翻译本指南!
了解如何设计大型系统。
准备系统设计面试。
学习如何设计可扩展的系统将帮助你成为一名更好的工程师。
系统设计是一个广泛的主题。网络上有大量关于系统设计原则的资源。
此存储库是有组织的资源集合,可帮助你了解如何大规模构建系统。
这是一个不断更新的开源项目。
欢迎投稿!
除了编码面试之外,系统设计是许多科技公司技术面试流程的必要组成部分。
练习常见的系统设计面试问题,并将你的结果与示例解决方案进行比较:讨论、代码和图表。
面试准备的其他主题:
提供的 Anki 抽认卡组使用间隔重复来帮助你保留关键的系统设计概念。
非常适合在旅途中使用。
寻找资源来帮助你准备编码面试?
查看姊妹存储库交互式编码挑战,其中包含一个额外的 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 服务器,它集中内部服务并向公众提供统一的接口。在反向代理将服务器的响应返回给客户端之前,来自客户端的请求将转发到可以满足该请求的服务器。
其他好处包括:
通过将 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)
由于写入操作,直写是一个缓慢的整体操作,但仅写入数据的后续读取速度很快。用户在更新数据时通常比读取数据时更容忍延迟。缓存中的数据不会过时。
在后写中,应用程序执行以下操作:
你可以将缓存配置为在过期之前自动刷新任何最近访问的缓存条目。
如果缓存可以准确预测将来可能需要哪些项目,则与直读相比,预刷新可以减少延迟。
异步工作流有助于减少原本以内联方式执行的昂贵操作的请求时间。他们还可以通过提前完成耗时的工作来提供帮助,例如定期汇总数据。
消息队列接收、保留和传递消息。如果操作太慢而无法以内联方式执行,则可以将消息队列与以下工作流一起使用:
用户不会被阻止,作业在后台处理。在此期间,客户端可能会选择性地执行少量处理,以使其看起来像任务已完成。例如,如果发布一条推文,该推文可以立即发布到你的时间线,但可能需要一些时间才能将你的推文实际发送给所有关注者。
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 /resign { “personid”: “1234” } |
删除 /人/1234 |
读人 | GET /readPerson?personid=1234 | 获取 /人/1234 |
读取人员的项目列表 | GET /readUsersItemsList?personid=1234 | 获取 /人/1234/项 |
将项目添加到人员的项目 |
POST /addItemToUsersItemsList { “personid”: “1234”; ”项“: ”456” } |
POST /persons/1234/items { “itemid”: “456” } |
更新项目 |
POST /modifyItem { “itemid”: “456”; ”键“:”值” } |
PUT /items/456 { “键”: “值” } |
删除项目 |
POST /removeItem {“itemid” : “456” } |
删除 /项目/456 |
本部分可以使用一些更新。考虑贡献!
安全性是一个广泛的话题。除非你有丰富的经验、安全背景或正在申请需要安全知识的职位,否则你可能只需要了解基础知识:
你有时会被要求进行“粗略”估算。例如,你可能需要确定从磁盘生成 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 |
设计一个像谷歌一样的搜索引擎 |
queue.acm.org stackexchange.com ardendertat.com stanford.edu |
设计一个可扩展的网络爬虫,如谷歌 | quora.com |
设计谷歌文档 |
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 |
设计脸书新闻提要功能 |
quora.com quora.com slideshare.net |
设计脸书时间轴功能 |
facebook.com highscalability.com |
设计脸书聊天功能 |
erlang-factory.com facebook.com |
设计一个像Facebook一样的图形搜索函数 |
facebook.com facebook.com facebook.com |
设计像CloudFlare这样的内容交付网络 | figshare.com |
设计一个像Twitter这样的热门话题系统 |
michael-noll.com 斯尼科洛夫 .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/ |
设计证券交易所(如纳斯达克或币安) |
简街 戈朗实施 去实现 |
添加系统设计问题 | 贡献 |
关于如何设计现实世界系统的文章。
不要关注以下文章的细节,而是:
类型 | 系统 | 参考资料 |
---|---|---|
数据处理 | MapReduce - 来自谷歌的分布式数据处理 | research.google.com |
数据处理 | Spark - 来自Databricks的分布式数据处理 | slideshare.net |
数据处理 | Storm - 来自Twitter的分布式数据处理 | slideshare.net |
数据存储 | Bigtable - 来自谷歌的分布式面向列的数据库 | harvard.edu |
数据存储 | HBase - Bigtable 的开源实现 | slideshare.net |
数据存储 | Cassandra - 来自Facebook的分布式面向列的数据库 | slideshare.net |
数据存储 | DynamoDB - 来自 Amazon 的面向文档的数据库 | harvard.edu |
数据存储 | MongoDB - 面向文档的数据库 | slideshare.net |
数据存储 | Spanner - 来自谷歌的全球分布式数据库 | research.google.com |
数据存储 | Memcached - 分布式内存缓存系统 | slideshare.net |
数据存储 | Redis - 具有持久性和值类型的分布式内存缓存系统 | slideshare.net |
文件系统 | 谷歌文件系统 (GFS) - 分布式文件系统 | research.google.com |
文件系统 | Hadoop File System (HDFS) - GFS 的开源实现 | apache.org |
杂项 | 胖胖 - 谷歌松散耦合分布式系统的锁定服务 | research.google.com |
杂项 | Dapper - 分布式系统跟踪基础结构 | research.google.com |
杂项 | Kafka - 来自LinkedIn的发布/订阅消息队列 | slideshare.net |
杂项 | Zookeeper - 支持同步的集中式基础架构和服务 | slideshare.net |
添加体系结构 | 贡献 |
公司 | 参考资料 |
---|---|
亚马逊河 | 亚马逊架构 |
辛奇卡斯特 | 每天制作 1,500 小时的音频 |
数据筛 | 实时数据挖掘 每秒 120,000 条推文 |
嘶�� | 我们如何扩展 Dropbox |
ESPN | 以每秒 100,000 duh nuh nuhs 的速度运行 |
谷歌 | 谷歌架构 |
1400 万用户,TB 级照片 是什么为Instagram提供动力 |
|
Justin.tv | 贾斯汀电视的直播视频广播架构 |
脸书 |
在Facebook TAO上扩展memcached:Facebook用于社交图谱 的分布式数据存储 Facebook的照片存储 Facebook Live如何同时向800,000名观众进行流式传输 |
Flickr | Flickr 架构 |
邮箱 | 6 周内从 0 到 100 万用户 |
网飞 |
整个 Netflix 堆栈 的 360 度视图 Netflix:按下播放时会发生什么? |
从 0 到 10 每月数十亿次页面浏览 量 1800 万访问者,10 倍增长,12 名员工 |
|
玩鱼 | 每月 5000 万用户,并且还在不断增长 |
丰富的鱼 | 丰富的鱼类架构 |
销售队伍 | 他们每天如何处理 13 亿笔交易 |
堆栈溢出 | 堆栈溢出架构 |
猫途鹰 | 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 存储库的经验教训 |
微信 | 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/