*[English](README.md) ∙ [日本語](README-ja.md) ∙ [简体中文](README-zh-Hans.md) ∙ [繁體中文](README-zh-TW.md) | [العَرَبِيَّة](https://github.com/donnemartin/system-design-primer/issues/170) ∙ [বাংলা](https://github.com/donnemartin/system-design-primer/issues/220) ∙ [Português do Brasil](https://github.com/donnemartin/system-design-primer/issues/40) ∙ [Deutsch](https://github.com/donnemartin/system-design-primer/issues/186) ∙ [ελληνικά](https://github.com/donnemartin/system-design-primer/issues/130) ∙ [עברית](https://github.com/donnemartin/system-design-primer/issues/272) ∙ [Italiano](https://github.com/donnemartin/system-design-primer/issues/104) ∙ [한국어](https://github.com/donnemartin/system-design-primer/issues/102) ∙ [فارسی](https://github.com/donnemartin/system-design-primer/issues/110) ∙ [Polski](https://github.com/donnemartin/system-design-primer/issues/68) ∙ [русский язык](https://github.com/donnemartin/system-design-primer/issues/87) ∙ [Español](https://github.com/donnemartin/system-design-primer/issues/136) ∙ [ภาษาไทย](https://github.com/donnemartin/system-design-primer/issues/187) ∙ [Türkçe](https://github.com/donnemartin/system-design-primer/issues/39) ∙ [tiếng Việt](https://github.com/donnemartin/system-design-primer/issues/127) ∙ [Français](https://github.com/donnemartin/system-design-primer/issues/250) | [添加翻译](https://github.com/donnemartin/system-design-primer/issues/28)*
**帮助[翻译](TRANSLATIONS.md)本指南!**
# 系统设计入门
## 动机
### 学习如何设计大规模系统
学习如何设计可扩展系统将帮助你成为一名更优秀的工程师。
系统设计是一个广泛的话题。网络上**散布着大量**关于系统设计原则的资源。
本仓库是一个**有组织的合集**,旨在帮助你学习如何构建大规模系统。
### 向开源社区学习
这是一个持续更新的开源项目。
欢迎[贡献](#contributing)!
### 为系统设计面试做准备
除了编程面试外,系统设计是许多科技公司**技术面试流程**中的一个**必要组成部分**。
**练习常见的系统设计面试题**,并将你的结果与**示例解答**进行**对比**:讨论、代码和图表。
面试准备的其他主题:
* [学习指南](#study-guide)
* [如何应对系统设计面试题](#how-to-approach-a-system-design-interview-question)
* [系统设计面试题,**含解答**](#system-design-interview-questions-with-solutions)
* [面向对象设计面试题,**含解答**](#object-oriented-design-interview-questions-with-solutions)
* [额外的系统设计面试题](#additional-system-design-interview-questions)
## Anki 抽认卡
提供的 [Anki 抽认卡牌组](https://apps.ankiweb.net/)使用间隔重复法帮助你记忆关键系统设计概念。
* [系统设计卡组](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/System%20Design.apkg)
* [系统设计练习卡组](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/System%20Design%20Exercises.apkg)
* [面向对象设计练习卡组](https://github.com/donnemartin/system-design-primer/tree/master/resources/flash_cards/OO%20Design.apkg)
非常适合在移动中使用。
### 编程资源:交互式编程挑战
寻找资源帮助你准备 [**编程面试**](https://github.com/donnemartin/interactive-coding-challenges)?
查看姐妹仓库 [**交互式编程挑战**](https://github.com/donnemartin/interactive-coding-challenges),其中包含额外的 Anki 卡组:
* [编程卡组](https://github.com/donnemartin/interactive-coding-challenges/tree/master/anki_cards/Coding.apkg)
## 系统设计主题索引
* [系统设计主题:从这里开始](#system-design-topics-start-here)
* [第 1 步:复习可扩展性视频讲座](#step-1-review-the-scalability-video-lecture)
* [第 2 步:复习可扩展性文章](#step-2-review-the-scalability-article)
* [后续步骤](#next-steps)
* [性能与可扩展性](#performance-vs-scalability)
* [延迟与吞吐量](#latency-vs-throughput)
* [可用性与一致性](#availability-vs-consistency)
* [CAP 定理](#cap-theorem)
* [CP - 一致性和分区容错性](#cp---consistency-and-partition-tolerance)
* [AP - 可用性和分区容错性](#ap---availability-and-partition-tolerance)
* [一致性模式](#consistency-patterns)
* [弱一致性](#weak-consistency)
* [最终一致性](#eventual-consistency)
* [强一致性](#strong-consistency)
* [可用性模式](#availability-patterns)
* [故障转移](#fail-over)
* [复制](#replication)
* [可用性数值](#availability-in-numbers)
* [域名系统](#domain-name-system)
* [内容分发网络](#content-delivery-network)
* [推送 CDN](#push-cdns)
* [拉取 CDN](#pull-cdns)
* [负载均衡器](#load-balancer)
* [主备](#active-passive)
* [主主](#active-active)
* [四层负载均衡](#layer-4-load-balancing)
* [七层负载均衡](#layer-7-load-balancing)
* [水平扩展](#horizontal-scaling)
* [反向代理(Web 服务器)](#reverse-proxy-web-server)
* [负载均衡器与反向代理](#load-balancer-vs-reverse-proxy)
* [应用层](#application-layer)
* [微服务](#microservices)
* [服务发现](#service-discovery)
* [数据库](#database)
* [关系型数据库管理系统 (RDBMS)](#relational-database-management-system-rdbms)
* [主从复制](#master-slave-replication)
* [主主复制](#master-master-replication)
* [联邦](#federation)
* [分片](#sharding)
* [反范式化](#denormalization)
* [SQL 调优](#sql-tuning)
* [NoSQL](#nosql)
* [键值存储](#key-value-store)
* [文档存储](#document-store)
* [列式存储](#wide-column-store)
* [图数据库](#graph-database)
* [SQL 还是 NoSQL](#sql-or-nosql)
* [缓存](#cache)
* [客户端缓存](#client-caching)
* [CDN 缓存](#cdn-caching)
* [Web 服务器缓存](#web-server-caching)
* [数据库缓存](#database-caching)
* [应用缓存](#application-caching)
* [数据库查询层缓存](#caching-at-the-database-query-level)
* [对象级缓存](#caching-at-the-object-level)
* [何时更新缓存](#when-to-update-the-cache)
* [缓存旁路](#cache-aside)
* [直写](#write-through)
* [回写](#write-behind-write-back)
* [预刷新](#refresh-ahead)
* [异步](#asynchronism)
* [消息队列](#message-queues)
* [任务队列](#task-queues)
* [背压](#back-pressure)
* [通信](#communication)
* [传输控制协议 (TCP)](#transmission-control-protocol-tcp)
* [用户数据报协议 (UDP)](#user-datagram-protocol-udp)
* [远程过程调用 (RPC)](#remote-procedure-call-rpc)
* [表述性状态转移 (REST)](#representational-state-transfer-rest)
* [安全](#security)
* [附录](#appendix)
* [2 的幂次表](#powers-of-two-table)
* [每个程序员都应该知道的延迟数](#latency-numbers-every-programmer-should-know)
* [额外的系统设计面试题](#additional-system-design-interview-questions)
* [现实世界架构](#real-world-architectures)
* [公司架构](#company-architectures)
* [公司工程博客](#company-engineering-blogs)
* [开发中](#under-development)
* [致谢](#credits)
* [联系信息](#contact-info)
* [许可](#license)
## 学习指南

**问:对于面试,我需要知道这里的所有内容吗?**
**答:不,你不需要为了准备面试而知道这里的所有内容**。
你在面试中被问到什么取决于以下变量:
* 你有多少经验
* 你的技术背景是什么
* 你正在面试什么职位
* 你正在面试哪些公司
* 运气
更资深的候选人通常被期望了解更多的系统设计知识。架构师或团队主管可能被期望比个人贡献者知道得更多。顶尖科技公司可能会有多轮设计面试。
从广泛开始,然后在几个领域深入。了解各种关键系统设计主题的一点知识会有所帮助。根据你的时间表、经验、你正在面试的职位以及你正在面试的公司,调整以下指南。
* **短期** - 旨在对系统设计主题有**广度**的了解。通过解决**一些**面试题来练习。
* **中期** - 旨在对系统设计主题有**广度**和**一定深度**的了解。通过解决**许多**面试题来练习。
* **长期** - 旨在对系统设计主题有**广度**和**更多深度**的了解。通过解决**大多数**面试题来练习。
| | 短期 | 中期 | 长期 |
|---|---|---|---|
| 通读 [系统设计主题](#index-of-system-design-topics) 以广泛了解系统如何工作 | :+1: | :+1: | :+1: |
| 通读你正在面试的公司的 [公司工程博客](#company-engineering-blogs) 中的一些文章 | :+1: | :+1: | :+1: |
| 通读一些 [现实世界架构](#real-world-architectures) | :+1: | :+1: | :+1: |
| 复习 [如何应对系统设计面试题](#how-to-approach-a-system-design-interview-question) | :+1: | :+1: | :+1: |
| 练习 [系统设计面试题与解答](#system-design-interview-questions-with-solutions) | 一些 | 许多 | 大多数 |
| 练习 [面向对象设计面试题与解答](#object-oriented-design-interview-questions-with-solutions) | 一些 | 许多 | 大多数 |
| 复习 [额外的系统设计面试题](#additional-system-design-interview-questions) | 一些 | 许多 | 大多数 |
## 如何应对系统设计面试题
系统设计面试是一个**开放式的对话**。你需要主导它。
你可以使用以下步骤来引导讨论。为了帮助巩固这个过程,请使用以下步骤练习 [系统设计面试题与解答](#system-design-interview-questions-with-solutions) 部分。
### 第 1 步:概述用例、约束和假设
收集需求并界定问题范围。提出问题以明确用例和约束。讨论假设。
* 谁将使用它?
* 他们将如何使用它?
* 有多少用户?
* 系统做什么?
* 系统的输入和输出是什么?
* 我们预计处理多少数据?
* 我们预计每秒有多少请求?
* 预期的读写比例是多少?
### 第 2 步:创建高层设计
概述包含所有重要组件的高层设计。
* 勾勒主要组件和连接
* 论证你的想法
### 第 3 步:设计核心组件
深入每个核心组件的细节。例如,如果你被要求 [设计一个短网址服务](solutions/system_design/pastebin/README.md),请讨论:
* 生成并存储完整 URL 的哈希值
* [MD5](solutions/system_design/pastebin/README.md) 和 [Base62](solutions/system_design/pastebin/README.md)
* 哈希冲突
* SQL 还是 NoSQL
* 数据库模式
* 将哈希 URL 转换为完整 URL
* 数据库查找
* API 和面向对象设计
### 第 4 步:扩展设计
在给定约束的情况下,识别并解决瓶颈。例如,你需要以下内容来解决可扩展性问题吗?
* 负载均衡器
* 水平扩展
* 缓存
* 数据库分片
讨论潜在的解决方案和权衡。一切都是权衡。使用 [可扩展系统设计原则](#index-of-system-design-topics) 来解决瓶颈。
### 粗略计算
你可能被要求手动进行一些估算。请参阅 [附录](#appendix) 中的以下资源:
* [使用粗略计算](http://highscalability.com/blog/2011/1/26/google-pro-tip-use-back-of-the-envelope-calculations-to-choo.html)
* [2 的幂次表](#powers-of-two-table)
* [每个程序员都应该知道的延迟数](#latency-numbers-every-programmer-should-know)
### 来源和延伸阅读
查看以下链接以更好地了解预期内容:
* [如何在系统设计面试中脱颖而出](https://web.archive.org/web/20210505130322/https://www.palantir.com/2011/10/how-to-rock-a-systems-design-interview/)
* [系统设计面试](http://www.hiredintech.com/system-design)
* [架构与系统设计面试入门](https://www.youtube.com/watch?v=ZgdS0EUmn70)
* [系统设计模板](https://leetcode.com/discuss/career/229177/My-System-Design-Template)
## 系统设计面试题与解答
| 问题 | |
|---|---|
| 设计 Pastebin.com (或 Bit.ly) | [解答](solutions/system_design/pastebin/README.md) |
| 设计 Twitter 时间线和搜索(或 Facebook Feed 和搜索) | [解答](solutions/system_design/twitter/README.md) |
| 设计网络爬虫 | [解答](solutions/system_design/web_crawler/README.md) |
| 设计 Mint.com | [解答](solutions/system_design/mint/README.md) |
| 设计社交网络的数据结构 | [解答](solutions/system_design/social_graph/README.md) |
| 设计搜索引擎的键值存储 | [解答](solutions/system_design/query_cache/README.md) |
| 设计 Amazon 按类目销售排名功能 | [解答](solutions/system_design/sales_rank/README.md) |
| 设计一个可扩展至百万用户的 AWS 系统 | [解答](solutions/system_design/scaling_aws/README.md) |
| 添加系统设计问题 | [贡献](#contributing) |
### 设计 Pastebin.com (或 Bit)
[查看练习和解答](solutions/system_design/pastebin/README.md)

### 设计 Twitter 时间线和搜索(或 Facebook Feed 和搜索)
[查看练习和解答](solutions/system_design/twitter/README.md)

### 设计网络爬虫
[查看练习和解答](solutions/system_design/web_crawler/README.md)

### 设计 Mint.com
[查看练习和解答](solutions/system_design/mint/README.md)

### 设计社交网络的数据结构
[查看练习和解答](solutions/system_design/social_graph/README.md)

### 设计搜索引擎的键值存储
[查看练习和解答](solutions/system_design/query_cache/README.md)

### 设计 Amazon 按类目销售排名功能
[查看练习和解答](solutions/system_design/sales_rank/README.md)

### 设计一个可扩展至百万用户的 AWS 系统
[查看练习和解答](solutions/system_design/scaling_aws/README.md)

## 面向对象设计面试题与解答
| 问题 | |
|---|---|
| 设计哈希表 | [解答](solutions/object_oriented_design/hash_table/hash_map.ipynb) |
| 设计最近最少使用缓存 | [解答](solutions/object_oriented_design/lru_cache/lru_cache.ipynb) |
| 设计呼叫中心 | [解答](solutions/object_oriented_design/call_center/call_center.ipynb) |
| 设计一副牌 | [解答](solutions/object_oriented_design/deck_of_cards/deck_of_cards.ipynb) |
| 设计停车场 | [解答](solutions/object_oriented_design/parking_lot/parking_lot.ipynb) |
| 设计聊天服务器 | [解答](solutions/object_oriented_design/online_chat/online_chat.ipynb) |
| 设计循环数组 | [贡献](#contributing) |
| 添加面向对象设计问题 | [贡献](#contributing) |
## 系统设计主题:从这里开始
系统设计新手?
首先,你需要对通用原则有一个基本的了解,学习它们是什么、如何使用以及它们的优缺点。
### 第 1 步:复习可扩展性视频讲座
[哈佛可扩展性讲座](https://www.youtube.com/watch?v=-W9F__D3oY4)
* 涵盖的主题:
* 垂直扩展
* 水平扩展
* 缓存
* 负载均衡
* 数据库复制
* 数据库分区
### 第 2 步:复习可扩展性文章
[可扩展性](https://web.archive.org/web/20221030091841/http://www.lecloud.net/tagged/scalability/chrono)
* 涵盖的主题:
* [克隆](https://web.archive.org/web/20220530193911/https://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [数据库](https://web.archive.org/web/20220602114024/https://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [缓存](https://web.archive.org/web/20230126233752/https://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [异步](https://web.archive.org/web/20220926171507/https://www.lecloud.net/post/9699762917/scalability-for-dummies-part-4-asynchronism)
### 后续步骤
接下来,我们将看看高层权衡:
* **性能** vs **可扩展性**
* **延迟** vs **吞吐量**
* **可用性** vs **一致性**
请记住,**一切都是权衡**。
然后我们将深入探讨更具体的主题,如 DNS、CDN 和负载均衡器。
## 性能与可扩展性
如果服务随着资源的增加而导致**性能**成比例地提高,则该服务是**可扩展的**。通常,提高性能意味着服务更多的工作单元,但也可以是处理更大的工作单元,例如当数据集增长时。
1
另一种看待性能与可扩展性的方式:
* 如果你有**性能**问题,你的系统对于单个用户来说很慢。
* 如果你有**可扩展性**问题,你的系统对于单个用户来说很快,但在高负载下很慢。
### 来源和延伸阅读
* [关于可扩展性](http://www.allthingsdistributed.com/2006/03/a_word_on_scalability.html)
* [可扩展性、可用性、稳定性、模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
## 延迟与吞吐量
**延迟**是执行某个动作或产生某个结果所需的时间。
**吞吐量**是单位时间内此类动作或结果的数量。
通常,你应该以**可接受的延迟**下的**最大吞吐量**为目标。
### 来源和延伸阅读
* [理解延迟与吞吐量](https://community.cadence.com/cadence_blogs_8/b/fv/posts/understanding-latency-vs-throughput)
## 可用性与一致性
### CAP 定理
Source: CAP theorem revisited
在分布式计算机系统中,你只能支持以下保证中的两项:
* **一致性** - 每次读取都能获得最近写入的数据或错误
* **可用性** - 每次请求都能获得响应(不保证包含最新版本的信息)
* **分区容错性** - 尽管出现任意分区(由于网络故障),系统仍继续运行
*网络是不可靠的,因此你需要支持分区容错性。你需要在一致性和可用性之间做出软件权衡。*
#### CP - 一致性和分区容错性
等待分区节点的响应可能会导致超时错误。如果你的业务需要原子读写,CP 是一个不错的选择。
#### AP - 可用性和分区容错性
响应返回任何节点上可用的最新版本数据,但这可能不是最新的。当分区解决后,写入可能需要一些时间才能传播。
如果业务需要允许[最终一致性](#eventual-consistency),或者系统需要在外部错误的情况下继续工作,AP 是一个不错的选择。
### 来源和延伸阅读
* [重访 CAP 定理](https://robertgreiner.com/cap-theorem-revisited/))
* [CAP 定理的通俗英语介绍](http://ksat.me/a-plain-english-introduction-to-cap-theorem)
* [CAP FAQ](https://github.com/henryr/cap-faq)
* [CAP 定理](https://www.youtube.com/watch?v=k-Yaq8AHlFA)
## 一致性模式
对于同一数据的多个副本,我们面临着如何同步它们的选择,以便客户端对数据有一致的视图。回顾 [CAP 定理](#cap-theorem) 中一致性的定义——每次读取都能获得最近写入的数据或错误。
### 弱一致性
写入后,读取可能会也可能不会看到它。采取尽力而为的方法。
这种方法见于 memcached 等系统。弱一致性适用于 VoIP、视频聊天和实时多人游戏等实时用例。例如,如果你正在通话并且信号丢失了几秒钟,当你重新连接时,你将听不到连接丢失期间所说的话。
### 最终一致性
写入后,读取最终会看到它(通常在毫秒内)。数据是异步复制的。
这种方法见于 DNS 和电子邮件等系统。最终一致性适用于高度可用的系统。
### 强一致性
写入后,读取将立即看到它。数据是同步复制的。
这种方法见于文件系统和 RDBMS。强一致性适用于需要事务的系统。
### 来源和延伸阅读
* [跨数据中心的事务](http://snarfed.org/transactions_across_datacenters_io.html)
## 可用性模式
有两种互补的模式来支持高可用性:**故障转移**和**复制**。
### 故障转移
#### 主备
使用主备故障转移,主服务器和备用服务器之间会发送心跳。如果心跳中断,备用服务器将接管主服务器的 IP 地址并恢复服务。
停机时间取决于备用服务器是已经处于“热”待机状态还是需要从“冷”待机状态启动。只有主服务器处理流量。
主备故障转移也被称为“主从故障转移”。
#### 主主
在主主模式下,两台服务器都管理流量,在它们之间分担负载。
如果服务器面向公众,DNS 需要知道两台服务器的公共 IP。如果服务器面向内部,应用逻辑需要知道这两台服务器。
主主故障转移也被称为“主主故障转移”。
### 缺点:故障转移
* 故障转移增加了更多硬件和额外的复杂性。
* 如果主系统在任何新写入的数据复制到备用系统之前发生故障,则有可能丢失数据。
### 复制
#### 主从和主主
此主题在 [数据库](#database) 部分有进一步讨论:
* [主从复制](#master-slave-replication)
* [主主复制](#master-master-replication)
### 可用性数值
可用性通常通过服务可用时间(或停机时间)占总时间的百分比来量化。可用性通常以“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)
```
如果 `Foo` 和 `Bar` 各自的可用性都为 99.9%,则它们串联后的总可用性为 99.8%。
###### 并行
当两个可用性 < 100% 的组件并联时,整体可用性会提高:
```
Availability (Total) = 1 - (1 - Availability (Foo)) * (1 - Availability (Bar))
```
如果 `Foo` 和 `Bar` 各自的可用性都为 99.9%,则它们并联后的总可用性为 99.9999%。
## 域名系统
Source: DNS security presentation
域名系统 (DNS) 将域名(如 www.example.com)转换为 IP 地址。
DNS 是分层的,顶层有少数权威服务器。你的路由器或 ISP 提供有关在进行查找时联系哪些 DNS 服务器的信息。较低级别的 DNS 服务器会缓存映射,由于 DNS 传播延迟,这些映射可能会变得陈旧。DNS 结果也可以被你的浏览器或操作系统缓存一段时间,具体时间由 [生存时间 (TTL)](https://en.wikipedia.org/wiki/Time_to_live) 决定。
* **NS 记录(名称服务器)** - 指定你域/子域的 DNS 服务器。
* **MX 记录(邮件交换)** - 指定用于接受消息的邮件服务器。
* **A 记录(地址)** - 将名称指向 IP 地址。
* **CNAME(规范名称)** - 将名称指向另一个名称或 `CNAME`(将 example.com 指向 www.example.com)或指向 `A` 记录。
[CloudFlare](https://www.cloudflare.com/dns/) 和 [Route 53](https://aws.amazon.com/route53/) 等服务提供托管 DNS 服务。一些 DNS 服务可以通过各种方法路由流量:
* [加权轮询](https://www.jscape.com/blog/load-balancing-algorithms)
* 防止流量流向维护中的服务器
* 平衡不同集群大小之间的流量
* A/B 测试
* [基于延迟](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-latency.html)
* [基于地理位置](https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/routing-policy-geo.html)
### 缺点:DNS
* 访问 DNS 服务器会引入轻微的延迟,尽管可以通过上述缓存来缓解。
* DNS 服务器管理可能很复杂,通常由 [政府、ISP 和大公司](http://superuser.com/questions/472695/who-controls-the-dns-servers/472729) 管理。
* DNS 服务最近遭受了 [DDoS 攻击](http://dyn.com/blog/dyn-analysis-summary-of-friday-october-21-attack/),导致用户在不知道 Twitter IP 地址的情况下无法访问 Twitter 等网站。
### 来源和延伸阅读
* [DNS 架构](https://technet.microsoft.com/en-us/library/dd197427(v=ws.10).aspx)
* [维基百科](https://en.wikipedia.org/wiki/Domain_Name_System)
* [DNS 文章](https://support.dnsimple.com/categories/dns/)
## 内容分发网络
Source: Why use a CDN
内容分发网络 (CDN) 是一个全球分布的代理服务器网络,从离用户更近的位置提供内容。通常,HTML/CSS/JS、照片和视频等静态文件由 CDN 提供,尽管 Amazon 的 CloudFront 等 CDN 支持动态内容。站点的 DNS 解析将告诉客户端联系哪个服务器。
从 CDN 提供内容可以通过两种方式显著提高性能:
* 用户从离他们近的数据中心接收内容
* 你的服务器不必处理由 CDN 完成的请求
### 推送 CDN
每当你的服务器发生更改时,推送 CDN 就会接收新内容。你全权负责提供内容,直接上传到 CDN 并重写 URL 以指向 CDN。你可以配置内容何时过期以及何时更新。内容仅在新内容或更改时上传,从而最大限度地减少流量,但最大化存储。
流量较少的站点或不常更新内容的站点非常适合推送 CDN。内容被放置在 CDN 上一次,而不是定期重新拉取。
### 拉取 CDN
当第一个用户请求内容时,拉取 CDN 从你的服务器获取新内容。你将内容保留在服务器上,并重写 URL 以指向 CDN。这会导致请求变慢,直到内容缓存在 CDN 上。
[生存时间 (TTL)](https://en.wikipedia.org/wiki/Time_to_live) 决定了内容缓存多长时间。拉取 CDN 最大限度地减少了 CDN 上的存储空间,但如果文件在实际更改之前过期并被拉取,则可能会产生冗余流量。
流量大的站点非常适合拉取 CDN,因为流量分布更均匀,只有最近请求的内容保留在 CDN 上。
### 缺点:CDN
* 根据流量,CDN 成本可能很高,尽管这应与不使用 CDN 产生的额外成本进行权衡。
* 如果内容在 TTL 过期之前更新,内容可能会过时。
* 需要更改静态内容的 URL 以指向 CDN。
### 来源和延伸阅读
* [全球分布式内容交付](https://figshare.com/articles/Globally_distributed_content_delivery/6605972)
* [推送和拉取 CDN 之间的区别](http://www.travelblogadvice.com/technical/the-differences-between-push-and-pull-cdns/)
* [维基百科](https://en.wikipedia.org/wiki/Content_delivery_network)
## 负载均衡器
Source: Scalable system design patterns
负载均衡器将传入的客户端请求分发到计算资源,如应用服务器和数据库。在每种情况下,负载均衡器将来自计算资源的响应返回给相应的客户端。负载均衡器在以下方面很有效:
* 防止请求进入不健康的服务器
* 防止资源过载
* 帮助消除单点故障
负载均衡器可以使用硬件(昂贵)实现,也可以使用 HAProxy 等软件实现。
额外的好处包括:
* **SSL 终止** - 解密传入的请求并加密服务器响应,以便后端服务器不必执行这些潜在昂贵的操作
* 无需在每个服务器上安装 [X.509 证书](https://en.wikipedia.org/wiki/X.509)
* **会话持久性** - 如果 Web 应用不跟踪会话,则发出 Cookie 并将特定客户端的请求路由到同一实例
为了防止故障,通常会设置多个负载均衡器,采用 [主备](#active-passive) 或 [主主](#active-active) 模式。
负载均衡器可以根据各种指标路由流量,包括:
* 随机
* 最少负载
* 会话/Cookie
* [轮询或加权轮询](https://www.g33kinfo.com/info/round-robin-vs-weighted-round-robin-lb)
* [四层](#layer-4-load-balancing)
* [七层](#layer-7-load-balancing)
### 四层负载均衡
四层负载均衡器查看 [传输层](#communication) 的信息来决定如何分发请求。通常,这涉及报头中的源、目标 IP 地址和端口,但不涉及数据包的内容。四层负载均衡器将网络数据包转发到上游服务器或从上游服务器接收数据包,执行 [网络地址转换 (NAT)](https://www.nginx.com/resources/glossary/layer-4-load-balancing/)。
### 七层负载均衡
七层负载均衡器查看 [应用层](#communication) 来决定如何分发请求。这可能涉及报头、消息和 Cookie 的内容。七层负载均衡器终止网络流量,读取消息,做出负载均衡决策,然后打开到所选服务器的连接。例如,七层负载均衡器可以将视频流量定向到托管视频的服务器,同时将更敏感的用户账单流量定向到经过安全加固的服务器。
以牺牲灵活性为代价,四层负载均衡比七层负载均衡需要更少的时间和计算资源,尽管在现代商用硬件上,性能影响可能微乎其微。
### 水平扩展
负载均衡器还可以帮助进行水平扩展,从而提高性能和可用性。使用商用机器进行横向扩展比在更昂贵的硬件上扩展单个服务器(称为**垂直扩展**)更具成本效益,并且可用性更高。聘请使用商用硬件的人才也比聘请专门的企业系统人才更容易。
#### 缺点:水平扩展
* 水平扩展引入了复杂性并涉及克隆服务器
* 服务器应该是无状态的:它们不应包含任何与用户相关的数据,如会话或个人资料图片
* 会话可以存储在集中式数据存储中,例如 [数据库](#database) (SQL, NoSQL) 或持久 [缓存](#cache) (Redis, Memcached)
* 下游服务器(如缓存和数据库)需要处理更多的并发连接,因为上游服务器会向外扩展
### 缺点:负载均衡器
* 如果负载均衡器没有足够的资源或配置不当,它可能会成为性能瓶颈。
* 引入负载均衡器以帮助消除单点故障会导致复杂性增加。
* 单个负载均衡器是一个单点故障,配置多个负载均衡器会进一步增加复杂性。
### 来源和延伸阅读
* [NGINX 架构](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy 架构指南](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [可扩展性](https://web.archive.org/web/20220530193911/https://www.lecloud.net/post/7295452622/scalability-for-dummies-part-1-clones)
* [维基百科](https://en.wikipedia.org/wiki/Load_balancing_(computing))
* [四层负载均衡](https://www.nginx.com/resources/glossary/layer-4-load-balancing/)
* [七层负载均衡](https://www.nginx.com/resources/glossary/layer-7-load-balancing/)
* [ELB 监听器配置](http://docs.aws.amazon.com/elasticloadbalancing/latest/classic/elb-listener-config.html)
## 反向代理(Web 服务器)
Source: Wikipedia
反向代理是一个 Web 服务器,它集中内部服务并向公众提供统一的接口。来自客户端的请求被转发到可以满足该请求的服务器,然后反向代理将服务器的响应返回给客户端。
额外的好处包括:
* **增强安全性** - 隐藏后端服务器的信息,将 IP 列入黑名单,限制每个客户端的连接数
* **提高可扩展性和灵活性** - 客户端只能看到反向代理的 IP,允许你扩展服务器或更改其配置
* **SSL 终止** - 解密传入的请求并加密服务器响应,以便后端服务器不必执行这些潜在昂贵的操作
* 无需在每个服务器上安装 [X.509 证书](https://en.wikipedia.org/wiki/X.509)
* **压缩** - 压缩服务器响应
* **缓存** - 返回缓存请求的响应
* **静态内容** - 直接提供静态内容
* HTML/CSS/JS
* 照片
* 视频
* 等
### 负载均衡器与反向代理
* 当你有多个服务器时,部署负载均衡器很有用。通常,负载均衡器将流量路由到执行相同功能的一组服务器。
* 即使只有一个 Web 服务器或应用服务器,反向代理也可能很有用,它可以开启上一节中描述的好处。
* NGINX 和 HAProxy 等解决方案可以同时支持七层反向代理和负载均衡。
### 缺点:反向代理
* 引入反向代理会导致复杂性增加。
* 单个反向代理是一个单点故障,配置多个反向代理(即 [故障转移](https://en.wikipedia.org/wiki/Failover))会进一步增加复杂性。
### 来源和延伸阅读
* [反向代理与负载均衡器](https://www.nginx.com/resources/glossary/reverse-proxy-vs-load-balancer/)
* [NGINX 架构](https://www.nginx.com/blog/inside-nginx-how-we-designed-for-performance-scale/)
* [HAProxy 架构指南](http://www.haproxy.org/download/1.2/doc/architecture.txt)
* [维基百科](https://en.wikipedia.org/wiki/Reverse_proxy)
## 应用层
Source: Intro to architecting systems for scale
将 Web 层与应用层(也称为平台层)分离,允许你独立扩展和配置这两层。添加新的 API 意味着添加应用服务器,而不必添加额外的 Web 服务器。**单一职责原则**提倡协同工作的小型自治服务。拥有小型服务的小型团队可以更积极地为快速增长制定计划。
应用层中的 Worker 还有助于实现 [异步](#asynchronism)。
### 微服务
与此讨论相关的是 [微服务](https://en.wikipedia.org/wiki/Microservices),它可以被描述为一套可独立部署的小型模块化服务。每个服务运行一个唯一的进程,并通过定义明确的轻量级机制进行通信,以服务于业务目标。
1
例如,Pinterest 可能具有以下微服务:用户个人资料、关注者、Feed、搜索、照片上传等。
### 服务发现
[Consul](https://www.consul.io/docs/index.html)、[Etcd](https://coreos.com/etcd/docs/latest) 和 [Zookeeper](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) 等系统可以通过跟踪注册的名称、地址和端口来帮助服务相互发现。[健康检查](https://www.consul.io/intro/getting-started/checks.html) 有助于验证服务完整性,通常使用 [HTTP](#hypertext-transfer-protocol-http) 端点完成。Consul 和 Etcd 都有一个内置的 [键值存储](#key-value-store),可用于存储配置值和其他共享数据。
### 缺点:应用层
* 添加具有松耦合服务的应用层需要从架构、运营和流程角度采用不同于单体系统的方法。
* 微服务在部署和运营方面可能会增加复杂性。
### 来源和延伸阅读
* [架构系统扩展入门](http://lethain.com/introduction-to-architecting-systems-for-scale)
* [破解系统设计面试](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [面向服务的架构](https://en.wikipedia.org/wiki/Service-oriented_architecture)
* [Zookeeper 简介](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper)
* [关于构建微服务你需要知道的事情](https://cloudncode.wordpress.com/2016/07/22/msa-getting-started/)
## 数据库
Source: Scaling up to your first 10 million users
### 关系型数据库管理系统 (RDBMS)
像 SQL 这样的关系数据库是组织在表中的数据项的集合。
**ACID** 是关系数据库 [事务](https://en.wikipedia.org/wiki/Database_transaction) 的一组属性。
* **原子性** - 每个事务要么全做,要么全不做
* **一致性** - 任何事务都将数据库从一种有效状态转换为另一种有效状态
* **隔离性** - 并发执行事务的结果与串行执行事务的结果相同
* **持久性** - 一旦事务提交,它将保持提交状态
有许多技术可以扩展关系数据库:**主从复制**、**主主复制**、**联邦**、**分片**、**反范式化**和 **SQL 调优**。
#### 主从复制
主服务器处理读取和写入,将写入复制到一个或多个从服务器,从服务器仅处理读取。从服务器也可以以树状方式复制到其他从服务器。如果主服务器离线,系统可以继续以只读模式运行,直到从服务器被提升为主服务器或配置了新的主服务器。
Source: Scalability, availability, stability, patterns
##### 缺点:主从复制
* 需要额外的逻辑将从服务器提升为主服务器。
* 有关与主从和主主**两者**相关的要点,请参阅 [缺点:复制](#disadvantages-replication)。
#### 主主复制
两个主服务器都处理读取和写入,并在写入时相互协调。如果任一主服务器宕机,系统可以继续处理读取和写入。
Source: Scalability, availability, stability, patterns
##### 缺点:主主复制
* 你需要一个负载均衡器,或者你需要更改应用逻辑以确定写入位置。
* 大多数主主系统要么是松散一致的(违反 ACID),要么由于同步而增加了写入延迟。
* 随着写入节点的增加和延迟的增加,冲突解决变得更加重要。
* 有关与主从和主主**两者**相关的要点,请参阅 [缺点:复制](#disadvantages-replication)。
##### 缺点:复制
* 如果主服务器在任何新写入的数据复制到其他节点之前发生故障,则有可能丢失数据。
* 写入被重放到读副本。如果有大量写入,读副本可能会因重放写入而不堪重负,无法进行那么多读取。
* 读从服务器越多,你需要复制的就越多,这会导致更大的复制延迟。
* 在某些系统上,写入主服务器可以生成多个线程以并行写入,而读副本仅支持使用单个线程顺序写入。
* 复制增加了更多的硬件和额外的复杂性。
##### 来源和延伸阅读:复制
* [可扩展性、可用性、稳定性、模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [多主复制](https://en.wikipedia.org/wiki/Multi-master_replication)
#### 联邦
Source: Scaling up to your first 10 million users
联邦(或功能分区)按功能拆分数据库。例如,你可以拥有三个数据库,而不是一个单一的、单体数据库:**论坛**、**用户**和**产品**,从而减少每个数据库的读写流量,从而减少复制延迟。较小的数据库导致更多数据可以放入内存,进而由于改进的缓存局部性而导致更多缓存命中。由于没有单一中央主服务器序列化写入,你可以并行写入,从而提高吞吐量。
##### 缺点:联邦
* 如果你的模式需要庞大的函数或表,则联邦无效。
* 你需要更新应用逻辑以确定要读取和写入哪个数据库。
* 使用 [服务器链接](http://stackoverflow.com/questions/5145637/querying-data-by-joining-two-tables-in-two-database-on-different-servers) 联接来自两个数据库的数据更复杂。
* 联邦增加了更多的硬件和额外的复杂性。
##### 来源和延伸阅读:联邦
* [扩展到你的第一个 1000 万用户](https://www.youtube.com/watch?v=kKjm4ehYiMs)
#### 分片
Source: Scalability, availability, stability, patterns
分片将数据分布到不同的数据库,使得每个数据库只能管理数据的子集。以用户数据库为例,随着用户数量的增加,会向集群添加更多分片。
与 [联邦](#federation) 的优势类似,分片导致更少的读写流量、更少的复制和更多的缓存命中。索引大小也减小了,这通常通过更快的查询来提高性能。如果一个分片宕机,其他分片仍然可以运行,尽管你需要添加某种形式的复制以避免数据丢失。像联邦一样,没有单一中央主服务器序列化写入,允许你并行写入并提高吞吐量。
分片用户表的常见方法是通过用户的姓氏首字母或用户的地理位置。
##### 缺点:分片
* 你需要更新应用逻辑以使用分片,这可能会导致复杂的 SQL 查询。
* 数据分布在分片中可能会变得不平衡。例如,分片上的一组超级用户可能会导致该分片与其他分片相比负载增加。
* 重新平衡增加了额外的复杂性。基于 [一致性哈希](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html) 的分片函数可以减少传输的数据量。
* 联接来自多个分片的数据更复杂。
* 分片增加了更多的硬件和额外的复杂性。
##### 来源和延伸阅读:分片
* [分片的到来](http://highscalability.com/blog/2009/8/6/an-unorthodox-approach-to-database-design-the-coming-of-the.html)
* [分片数据库架构](https://en.wikipedia.org/wiki/Shard_(database_architecture))
* [一致性哈希](http://www.paperplanes.de/2011/12/9/the-magic-of-consistent-hashing.html)
#### 反范式化
反范式化试图以牺牲一些写入性能为代价来提高读取性能。将数据的冗余副本写入表中,以避免昂贵的联接。一些 RDBMS(如 [PostgreSQL](https://en.wikipedia.org/wiki/PostgreSQL) 和 Oracle)支持 [物化视图](https://en.wikipedia.org/wiki/Materialized_view),它们处理存储冗余信息和保持冗余副本一致的工作。
一旦使用 [联邦](#federation) 和 [分片](#sharding) 等技术分发数据,管理跨数据中心的联接会进一步增加复杂性。反范式化可能会避免这种复杂联接的需要。
在大多数系统中,读取与写入的比例可能严重失调,达到 100:1 甚至 1000:1。导致复杂数据库联接的读取可能非常昂贵,在磁盘操作上花费大量时间。
##### 缺点:反范式化
* 数据重复。
* 约束可以帮助冗余的信息副本保持同步,但这增加了数据库设计的复杂性。
* 在高写入负载下的反范式化数据库的性能可能不如其范式化版本。
###### 来源和延伸阅读:反范式化
* [反范式化](https://en.wikipedia.org/wiki/Denormalization)
#### SQL 调优
SQL 调优是一个广泛的话题,许多 [书籍](https://www.amazon.com/s/ref=nb_sb_noss_2?url=search-alias%3Daps&field-keywords=sql+tuning) 已作为参考编写。
**基准测试**和**分析**对于模拟和发现瓶颈很重要。
* **基准测试** - 使用 [ab](http://httpd.apache.org/docs/2.2/programs/ab.html) 等工具模拟高负载情况。
* **分析** - 启用 [慢查询日志](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html) 等工具以帮助跟踪性能问题。
基准测试和分析可能会为你指出以下优化。
##### 收紧模式
* MySQL 以连续块的形式转储到磁盘以便快速访问。
* 对固定长度的字段使用 `CHAR` 而不是 `VARCHAR`。
* `CHAR` 有效地允许快速随机访问,而对于 `VARCHAR`,你必须在移动到下一个之前找到字符串的结尾。
* 对大块文本(如博客文章)使用 `TEXT`。`TEXT` 还允许布尔搜索。使用 `TEXT` 字段导致在磁盘上存储一个指针,用于定位文本块。
* 对高达 2^32 或 40 亿的较大数字使用 `INT`。
* 对货币使用 `DECIMAL` 以避免浮点表示错误。
* 避免存储大型 `BLOBS`,改为存储获取对象的位置。
* `VARCHAR(255)` 是可以在 8 位数字中计数的最大字符数,通常在某些 RDBMS 中最大化字节的使用。
* 在适用的地方设置 `NOT NULL` 约束以 [提高搜索性能](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)。
##### 使用良好的索引
* 你正在查询(`SELECT`、`GROUP BY`、`ORDER BY`、`JOIN`)的列可能通过索引会更快。
* 索引通常表示为自平衡 [B 树](https://en.wikipedia.org/wiki/B-tree),它保持数据排序并允许在对数时间内进行搜索、顺序访问、插入和删除。
* 放置索引可以将数据保留在内存中,但需要更多空间。
* 写入也可能变慢,因为索引也需要更新。
* 加载大量数据时,禁用索引、加载数据、然后重建索引可能会更快。
##### 避免昂贵的联接
* 在性能要求的地方 [反范式化](#denormalization)。
##### 分区表
* 通过将热点放在单独的表中来拆分表,以帮助将其保留在内存中。
##### 调整查询缓存
* 在某些情况下,[查询缓存](https://dev.mysql.com/doc/refman/5.7/en/query-cache.html) 可能导致 [性能问题](https://www.percona.com/blog/2016/10/12/mysql-5-7-performance-tuning-immediately-after-installation/)。
##### 来源和延伸阅读:SQL 调优
* [优化 MySQL 查询的技巧](http://aiddroid.com/10-tips-optimizing-mysql-queries-dont-suck/)
* [我经常看到使用 VARCHAR(255) 有什么充分的理由吗?](http://stackoverflow.com/questions/1217466/is-there-a-good-reason-i-see-varchar255-used-so-often-as-opposed-to-another-l)
* [空值如何影响性能?](http://stackoverflow.com/questions/1017239/how-do-null-values-affect-performance-in-a-database-search)
* [慢查询日志](http://dev.mysql.com/doc/refman/5.7/en/slow-query-log.html)
### NoSQL
NoSQL 是以 **键值存储**、**文档存储**、**列式存储**或 **图数据库**表示的数据项的集合。数据是反范式化的,联接通常在应用代码中完成。大多数 NoSQL 存储缺乏真正的 ACID 事务,并倾向于 [最终一致性](#eventual-consistency)。
**BASE** 通常用于描述 NoSQL 数据库的属性。与 [CAP 定理](#cap-theorem) 相比,BASE 选择可用性而不是一致性。
* **基本可用** - 系统保证可用性。
* **软状态** - 即使没有输入,系统的状态也可能随时间改变。
* **最终一致性** - 系统将在一段时间内变得一致,前提是系统在该期间内未接收到输入。
除了在 [SQL 或 NoSQL](#sql-or-nosql) 之间进行选择外,了解哪种类型的 NoSQL 数据库最适合你的用例也很有帮助。我们将在下一节中回顾 **键值存储**、**文档存储**、**列式存储**和 **图数据库**。
#### 键值存储
键值存储通常允许 O(1) 读写,并且通常由内存或 SSD 支持。数据存储可以按 [字典顺序](https://en.wikipedia.org/wiki/Lexicographical_order) 维护键,从而允许高效检索键范围。键值存储允许存储带有值的元数据。
键值存储提供高性能,通常用于简单的数据模型或快速变化的数据,例如内存缓存层。由于它们仅提供有限的操作集,如果需要其他操作,复杂性会转移到应用层。
键值存储是更复杂系统(如文档存储)的基础,在某些情况下是图数据库。
##### 来源和延伸阅读:键值存储
* [键值数据库](https://en.wikipedia.org/wiki/Key-value_database)
* [键值存储的缺点](http://stackoverflow.com/questions/4056093/what-are-the-disadvantages-of-using-a-key-value-table-over-nullable-columns-or)
* [Redis 架构](http://qnimate.com/overview-of-redis-architecture/)
* [Memcached 架构](https://adayinthelifeof.nl/2011/02/06/memcache-internals/)
#### 文档存储
文档存储以文档(XML、JSON、二进制等)为中心,其中文档存储给定对象的所有信息。文档存储提供 API 或查询语言以基于文档本身内部结构进行查询。*注意,许多键值存储包含用于处理值元数据的功能,模糊了这两种存储类型之间的界限。*
基于底层实现,文档按集合、标签、元数据或目录组织。尽管文档可以组织或分组在一起,但它们可能具有彼此完全不同的字段。
一些文档存储,如 [MongoDB](https://www.mongodb.com/mongodb-architecture) 和 [CouchDB](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/),也提供类似 SQL 的语言来执行复杂的查询。[DynamoDB](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) 支持键值和文档。
文档存储提供高灵活性,通常用于处理偶尔更改的数据。
##### 来源和延伸阅读:文档存储
* [面向文档的数据库](https://en.wikipedia.org/wiki/Document-oriented_database)
* [MongoDB 架构](https://www.mongodb.com/mongodb-architecture)
* [CouchDB 架构](https://blog.couchdb.org/2016/08/01/couchdb-2-0-architecture/)
* [Elasticsearch 架构](https://www.elastic.co/blog/found-elasticsearch-from-the-bottom-up)
#### 列式存储
Source: SQL & NoSQL, a brief history
列式存储的基本数据单位是列(名称/值对)。列可以按列族分组(类似于 SQL 表)。超级列族进一步对列族进行分组。你可以使用行键独立访问每列,具有相同行键的列形成一行。每个值都包含用于版本控制和冲突解决的时间戳。
Google 推出了 [Bigtable](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) 作为第一个列式存储,它影响了 Hadoop 生态系统中常用的开源 [HBase](https://www.edureka.co/blog/hbase-architecture/),以及来自 Facebook 的 [Cassandra](http://docs.datastax.com/en/cassandra/3.0/cassandra/architecture/archIntro.html)。BigTable、HBase 和 Cassandra 等存储按字典顺序维护键,从而允许高效检索选择性键范围。
列式存储提供高可用性和高可扩展性。它们通常用于非常大的数据集。
##### 来源和延伸阅读:列式存储
* [SQL 和 NoSQL 简史](http://blog.grio.com/2015/11/sql-nosql-a-brief-history.html)
* [Bigtable 架构](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf)
* [HBase 架构](https://www.edureka.co/blog/hbase-architecture/)
* [Cassandra 架构](http://docs.datastax.com/en/cassandra/3.0/cassandra/architecture/archIntro.html)
#### 图数据库
Source: Graph database
在图数据库中,每个节点都是一个记录,每条弧都是两个节点之间的关系。图数据库经过优化,可以表示具有许多外键或多对多关系的复杂关系。
图数据库为具有复杂关系的数据模型(如社交网络)提供高性能。它们相对较新,尚未被广泛使用;找到开发工具和资源可能更困难。许多图只能通过 [REST API](#representational-state-transfer-rest) 访问。
##### 来源和延伸阅读:图
* [图数据库](https://en.wikipedia.org/wiki/Graph_database)
* [Neo4j](https://neo4j.com/)
* [FlockDB](https://blog.twitter.com/2010/introducing-flockdb)
#### 来源和延伸阅读:NoSQL
* [BASE 术语解释](http://stackoverflow.com/questions/3342497/explanation-of-base-terminology)
* [NoSQL 数据库调查和决策指南](https://medium.com/baqend-blog/nosql-databases-a-survey-and-decision-guidance-ea7823a822d#.wskogqenq)
* [可扩展性](https://web.archive.org/web/20220602114024/https://www.lecloud.net/post/7994751381/scalability-for-dummies-part-2-database)
* [NoSQL 简介](https://www.youtube.com/watch?v=qI_g07C_Q5I)
* [NoSQL 模式](http://horicky.blogspot.com/2009/11/nosql-patterns.html)
### SQL 还是 NoSQL
Source: Transitioning from RDBMS to NoSQL
选择 **SQL** 的理由:
* 结构化数据
* 严格的模式
* 关系数据
* 需要复杂的联接
* 事务
* 明确的扩展模式
* 更成熟:开发人员、社区、代码、工具等
* 按索引查找非常快
选择 **NoSQL** 的理由:
* 半结构化数据
* 动态或灵活的模式
* 非关系数据
* 不需要复杂的联接
* 存储大量 TB(或 PB)数据
* 非常数据密集的工作负载
* 非常高的 IOPS 吞吐量
适合 NoSQL 的样本数据:
* 快速摄入点击流和日志数据
* 排行榜或评分数据
* 临时数据,例如购物车
* 经常访问的(“热”)表
* 元数据/查找表
##### 来源和延伸阅读:SQL 还是 NoSQL
* [扩展到你的第一个 1000 万用户](https://www.youtube.com/watch?v=kKjm4ehYiMs)
* [SQL 与 NoSQL 的区别](https://www.sitepoint.com/sql-vs-nosql-differences/)
## 缓存
Source: Scalable system design patterns
缓存可改善页面加载时间,并减少服务器和数据库的负载。在此模型中,调度器将首先查找之前是否已发出请求,并尝试找到以前的结果以返回,从而节省实际执行。
数据库通常受益于其分区中读取和写入的均匀分布。热门项目可能会使分布倾斜,从而导致瓶颈。在数据库前放置缓存有助于吸收不均匀的负载和流量峰值。
### 客户端缓存
缓存可以位于客户端(操作系统或浏览器)、[服务器端](#reverse-proxy-web-server) 或独立的缓存层。
### CDN 缓存
[CDN](#content-delivery-network) 被视为一种缓存。
### Web 服务器缓存
[反向代理](#reverse-proxy-web-server) 和缓存(如 [Varnish](https://www.varnish-cache.org/))可以直接提供静态和动态内容。Web 服务器也可以缓存请求,返回响应而无需联系应用服务器。
### 数据库缓存
你的数据库通常在默认配置中包含某种级别的缓存,针对通用用例进行了优化。针对特定使用模式调整这些设置可以进一步提高性能。
### 应用缓存
内存缓存(如 Memcached 和 Redis)是应用和数据存储之间的键值存储。由于数据保存在 RAM 中,因此比数据存储在磁盘上的典型数据库要快得多。RAM 比磁盘更有限,因此 [缓存失效](https://en.wikipedia.org/wiki/Cache_algorithms) 算法(如 [最近最少使用 (LRU)](https://en.wikipedia.org/wiki/Cache_replacement_policies#Least_recently_used_(LRU))有助于使“冷”条目失效并将“热”数据保留在 RAM 中。
Redis 具有以下附加功能:
* 持久化选项
* 内置数据结构,例如排序集合和列表
你可以缓存多个级别,分为两大类:**数据库查询**和**对象**:
* 行级别
* 查询级别
* 完全形成的可序列化对象
* 完全渲染的 HTML
通常,你应该尽量避免基于文件的缓存,因为它使克隆和自动扩展变得更加困难。
### 数据库查询级缓存
每当查询数据库时,将查询作为键进行哈希处理,并将结果存储在缓存中。这种方法会遇到过期问题:
* 很难删除具有复杂查询的缓存结果
* 如果一段数据(如表单元格)发生变化,则需要删除所有可能包含已更改单元格的缓存查询
### 对象级缓存
将你的数据视为对象,就像你在应用代码中所做的那样。让你的应用将数据集从数据库组装到类实例或数据结构中:
* 如果其基础数据已更改,则从缓存中删除该对象
* 允许异步处理:Worker 通过使用最新的缓存对象来组装对象
关于缓存内容的建议:
* 用户会话
* 完全渲染的网页
* 活动流
* 用户图数据
### 何时更新缓存
由于你只能在缓存中存储有限的数据量,因此你需要确定哪种缓存更新策略最适合你的用例。
#### 缓存旁路
Source: From cache to in-memory data grid
应用程序负责从存储读取和写入。缓存不直接与存储交互。应用程序执行以下操作:
* 在缓存中查找条目,导致缓存未命中
* 从数据库加载条目
* 将条目添加到缓存
* 返回条目
[Memcached](https://memcached.org/) 通常以这种方式使用。
后续读取添加到缓存的数据的速度很快。缓存旁路也称为延迟加载。仅缓存请求的数据,这避免了使用未请求的数据填充缓存。
##### 缺点:缓存旁路
* 每次缓存未命中都会导致三次往返,这可能会导致明显的延迟。
* 如果数据库中的数据已更新,则数据可能会变得陈旧。通过设置强制更新缓存条目的生存时间 (TTL) 或使用直写,可以缓解此问题。
* 当节点发生故障时,它将被一个新的空节点替换,从而增加延迟。
#### 直写
Source: Scalability, availability, stability, patterns
应用程序将缓存用作主数据存储,从中读取和写入数据,而缓存负责读取和写入数据库:
* 应用程序在缓存中添加/更新条目
* 缓存同步将条目写入数据存储
* 返回
应用程序代码:
```
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)
```
直写由于写操作而是一个较慢的整体操作,但后续仅读取刚写入的数据的速度很快。用户在更新数据时通常比读取数据更能容忍延迟。缓存中的数据不会过时。
##### 缺点:直写
* 当由于故障或扩展而创建新节点时,新节点将不会缓存条目,直到在数据库中更新该条目。与直写结合使用的缓存旁路可以缓解此问题。
* 大多数写入的数据可能永远不会被读取,可以通过 TTL 最小化。
#### 回写
Source: Scalability, availability, stability, patterns
在回写中,应用程序执行以下操作:
* 在缓存中添加/更新条目
* 异步将条目写入数据存储,从而提高写入性能
##### 缺点:回写
* 如果缓存在其内容到达数据存储之前发生故障,则可能会丢失数据。
* 实现回写比实现缓存旁路或直写更复杂。
#### 预刷新
Source: From cache to in-memory data grid
你可以将缓存配置为在任何最近访问的缓存条目过期之前自动刷新它。
如果缓存可以准确预测将来可能需要哪些项目,则预刷新与直读相比可以减少延迟。
##### 缺点:预刷新
* 如果没有准确预测将来可能需要哪些项目,则可能会导致性能低于没有预刷新的情况。
### 缺点:缓存
* 需要通过 [缓存失效](https://en.wikipedia.org/wiki/Cache_algorithms) 保持缓存与事实来源(如数据库)之间的一致性。
* 缓存失效是一个难题,存在与何时更新缓存相关的额外复杂性。
* 需要进行应用更改,例如添加 Redis 或 memcached。
### 来源和延伸阅读
* [从缓存到内存数据网格](http://www.slideshare.net/tmatyashovsky/from-cache-to-in-memory-data-grid-introduction-to-hazelcast)
* [可扩展系统设计模式](http://horicky.blogspot.com/2010/10/scalable-system-design-patterns.html)
* [架构系统扩展入门](http://lethain.com/introduction-to-architecting-systems-for-scale/)
* [可扩展性、可用性、稳定性、模式](http://www.slideshare.net/jboner/scalability-availability-stability-patterns/)
* [可扩展性](https://web.archive.org/web/20230126233752/https://www.lecloud.net/post/9246290032/scalability-for-dummies-part-3-cache)
* [AWS ElastiCache 策略](http://docs.aws.amazon.com/AmazonElastiCache/latest/UserGuide/Strategies.html)
* [维基百科](https://en.wikipedia.org/wiki/Cache_(computing))
## 异步
Source: Intro to architecting systems for scale
异步工作流有助于减少原本需要内联执行的昂贵操作的请求时间。它们还可以通过提前完成耗时的工作来提供帮助,例如定期聚合数据。
### 消息队列
消息队列接收、保存和传递消息。如果一个操作太慢而无法内联执行,你可以使用具有以下工作流的消息队列:
* 应用程序将作业发布到队列,然后通知用户作业状态
* Worker 从队列中提取作业,对其进行处理,然后发出作业完成信号
用户没有被阻塞,作业在后台处理。在此期间,客户端可能会选择进行少量处理,使其看起来好像任务已完成。例如,如果发布推文,推文可能会立即发布到你的时间线,但可能需要一些时间才能将你的推文实际传递给你的所有关注者。
**[Redis](https://redis.io/)** 可用作简单的消息代理,但消息可能会丢失。
**[RabbitMQ](https://www.rabbitmq.com/)** 很受欢迎,但需要你适应 'AMQP' 协议并管理你自己的节点。
**[Amazon SQS](https://aws.amazon.com/sqs/)** 是托管的,但可能会有高延迟,并且有可能消息被传递两次。
### 任务队列
任务队列接收任务及其相关数据,运行它们,然后传递其结果。它们可以支持调度,并可用于在后台运行计算密集型作业。
**[Celery](https://docs.celeryproject.org/en/stable/)** 支持调度,并且主要具有 Python 支持。
### 背压
如果队列开始显著增长,队列大小可能会变得大于内存,从而导致缓存未命中、磁盘读取甚至更慢的性能。[背压](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html) 可以通过限制队列大小来提供帮助,从而保持高吞吐率和队列中已有作业的良好响应时间。一旦队列填满,客户端就会收到服务器繁忙或 HTTP 503 状态码,以便稍后重试。客户端可以在稍后重试请求,也许使用 [指数退避](https://en.wikipedia.org/wiki/Exponential_backoff)。
### 缺点:异步
* 诸如廉价计算和实时工作流之类的用例可能更适合同步操作,因为引入队列会增加延迟和复杂性。
### 来源和延伸阅读
* [这全是一个数字游戏](https://www.youtube.com/watch?v=1KRYH75wgy4)
* [在过载时应用背压](http://mechanical-sympathy.blogspot.com/2012/05/apply-back-pressure-when-overloaded.html)
* [利特尔法则](https://en.wikipedia.org/wiki/Little%27s_law)
* [消息队列和任务队列之间有什么区别?](https://www.quora.com/What-is-the-difference-between-a-message-queue-and-a-task-queue-Why-would-a-task-queue-require-a-message-broker-like-RabbitMQ-Redis-Celery-or-IronMQ-to-function)
## 通信
Source: OSI 7 layer model
### 超文本传输协议 (HTTP)
HTTP 是一种在客户端和服务器之间编码和传输数据的方法。它是一种请求/响应协议:客户端发出请求,服务器发出包含相关内容和有关请求完成状态信息的响应。HTTP 是自包含的,允许请求和响应流经许多执行负载均衡、缓存、加密和压缩的中间路由器和服务器。
基本的 HTTP 请求由动词(方法)和资源(端点)组成。以下是常见的 HTTP 动词:
| 动词 | 描述 | 幂等* | 安全 | 可缓存 |
|---|---|---|---|---|
| GET | 读取资源 | 是 | 是 | 是 |
| POST | 创建资源或触发处理数据的过程 | 否 | 否 | 如果响应包含新鲜度信息则为是 |
| PUT | 创建或替换资源 | 是 | 否 | 否 |
| PATCH | 部分更新资源 | 否 | 否 | 如果响应包含新鲜度信息则为是 |
| DELETE | 删除资源 | 是 | 否 | 否 |
*可以被多次调用而不产生不同的结果。
HTTP 是一种依赖低级协议(如 **TCP** 和 **UDP**)的应用层协议。
#### 来源和延伸阅读:HTTP
* [什么是 HTTP?](https://www.nginx.com/resources/glossary/http/)
* [HTTP 和 TCP 之间的区别](https://www.quora.com/What-is-the-difference-between-HTTP-protocol-and-TCP-protocol)
* [PUT 和 PATCH 之间的区别](https://laracasts.com/discuss/channels/general-discussion/whats-the-differences-between-put-and-patch?page=1)
### 传输控制协议 (TCP)
Source: How to make a multiplayer game
TCP 是基于 [IP 网络](https://en.wikipedia.org/wiki/Internet_Protocol) 的面向连接的协议。连接使用 [握手](https://en.wikipedia.org/wiki/Handshaking) 建立和终止。发送的所有数据包都保证通过以下方式以原始顺序到达目的地且无损坏:
* 每个数据包的序列号和 [校验和字段](https://en.wikipedia.org/wiki/Transmission_Control_Protocol#Checksum_computation)
* [确认](https://en.wikipedia.org/wiki/Acknowledgement_(data_networks)) 数据包和自动重传
如果发送方未收到正确的响应,它将重新发送数据包。如果出现多次超时,则连接被断开。TCP 还实现 [流量控制](https://en.wikipedia.org/wiki/Flow_control_(data)) 和 [拥塞控制](https://en.wikipedia.org/wiki/Network_congestion#Congestion_control). 这些保证会导致延迟,并且通常导致传输效率低于 UDP。
为了确保高吞吐量,Web 服务器可以保持大量 TCP 连接打开,从而导致高内存使用率。在 Web 服务器线程和(例如)[memcached](https://memcached.org/) 服务器之间拥有大量打开的连接可能很昂贵。[连接池](https://en.wikipedia.org/wiki/Connection_pool) 可以在适用的地方切换到 UDP 之外提供帮助。
TCP 对于需要高可靠性但对时间要求不高的应用很有用。一些示例包括 Web 服务器、数据库信息、SMTP、FTP 和 SSH。
在以下情况下使用 TCP 而不是 UDP:
* 你需要所有数据完整无缺地到达
* 你希望自动对网络吞吐量进行最佳估计利用
### 用户数据报协议 (UDP)
Source: How to make a multiplayer game
UDP 是无连接的。数据报(类似于数据包)仅在数据报级别得到保证。数据报可能会无序到达目的地或根本不到达。UDP 不支持拥塞控制。如果没有 TCP 支持的保证,UDP 通常更有效率。
UDP 可以广播,将数据报发送到子网上的所有设备。这对于 [DHCP](https://en.wikipedia.org/wiki/Dynamic_Host_Configuration_Protocol) 很有用,因为客户端尚未收到 IP 地址,从而阻止了 TCP 在没有 IP 地址的情况下进行流式传输的方式。
UDP 可靠性较低,但适用于实时用例,例如 VoIP、视频聊天、流媒体和实时多人游戏。
在以下情况下使用 UDP 而不是 TCP:
* 你需要最低的延迟
* 晚到的数据比数据丢失更糟糕
* 你想要实现自己的错误纠正
#### 来源和延伸阅读:TCP 和 UDP
* [游戏编程网络](http://gafferongames.com/networking-for-game-programmers/udp-vs-tcp/)
* [TCP 和 UDP 协议之间的主要区别](http://www.cyberciti.biz/faq/key-differences-between-tcp-and-udp-protocols/)
* [TCP 和 UDP 之间的区别](http://stackoverflow.com/questions/5970383/difference-between-tcp-and-udp)
* [传输控制协议](https://en.wikipedia.org/wiki/Transmission_Control_Protocol)
* [用户数据报协议](https://en.wikipedia.org/wiki/User_Datagram_Protocol)
* [在 Facebook 扩展 Memcache](http://www.cs.bu.edu/~jappavoo/jappavoo.github.com/451/papers/memcache-fb.pdf)
### 远程过程调用 (RPC)
Source: Crack the system design interview
在 RPC 中,客户端导致在不同的地址空间(通常是远程服务器)上执行过程。该过程被编码为就像它是本地过程调用一样,从而抽象出如何从客户端程序与服务器通信的详细信息。远程调用通常比本地调用更慢且可靠性更低,因此将 RPC 调用与本地调用区分开来很有帮助。流行的 RPC 框架包括 [Protobuf](https://developers.google.com/protocol-buffers/)、[Thrift](https://thrift.apache.org/) 和 [Avro](https://avro.apache.org/docs/current/)。
RPC 是一种请求-响应协议:
* **客户端程序** - 调用客户端存根过程。像本地过程调用一样,参数被压入堆栈。
* **客户端存根过程** - 将过程 ID 和参数封装(打包)到请求消息中。
* **客户端通信模块** - 操作系统将消息从客户端发送到服务器。
* **服务器通信模块** - 操作系统将传入的数据包传递给服务器存根过程。
* **服务器存根过程** - 拆包结果,调用与过程 ID 匹配的服务器过程并传递给定的参数。
* 服务器响应以相反的顺序重复上述步骤。
示例 RPC 调用:
```
GET /someoperation?data=anId
POST /anotheroperation
{
"data":"anId";
"anotherdata": "another value"
}
```
RPC 专注于公开行为。RPC 通常用于内部通信的性能原因,因为你可以手工制作本机调用以更好地适应你的用例。
在以下情况下选择本机库(即 SDK):
* 你知道你的目标平台。
* 你想要控制如何访问你的“逻辑”。
* 你想要控制如何处理库之外的错误控制。
* 性能和最终用户体验是你的首要任务。
遵循 **REST** 的 HTTP API 往往更多地用于公共 API。
#### 缺点:RPC
* RPC 客户端变得与服务实现紧密耦合。
* 必须为每个新操作或用例定义一个新的 API。
* 调试 RPC 可能很困难。
* 你可能无法利用现有技术。例如,可能需要额外的努力来确保 [RPC 调用在缓存服务器(如 Squid)上被正确缓存](https://web.archive.org/web/20170608193645/http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)](http://www.squid-cache.org/)。
### 表述性状态转移 (REST)
REST 是一种强制执行客户端/服务器模型的架构风格,其中客户端对服务器管理的一组资源进行操作。服务器提供资源和操作的表示,这些操作可以操作或获取资源的新表示。所有通信都必须是无状态和可缓存的。
RESTful 接口有四个品质:
* **识别资源(HTTP 中的 URI)** - 无论任何操作如何,都使用相同的 URI。
* **通过表示进行更改(HTTP 中的动词)** - 使用动词、报头和主体。
* **自描述错误消息(HTTP 中的状态响应)** - 使用状态码,不要重新发明轮子。
* **[HATEOAS](http://restcookbook.com/Basics/hateoas/) (HTTP 的 HTML 接口)** - 你的 Web 服务应该可以在浏览器中完全访问。
REST 调用示例:
```
GET /someresources/anId
PUT /someresources/anId
{"anotherdata": "another value"}
```
REST 专注于暴露数据。它最大程度地减少了客户端/服务器之间的耦合,常用于公共 HTTP API。REST 通过 URI、[通过头信息表示](https://github.com/for-GET/know-your-http-well/blob/master/headers.md) 以及通过 GET、POST、PUT、DELETE 和 PATCH 等动词执行操作,采用更通用和统一的方法来暴露资源。由于是无状态的,REST 非常适合水平扩展和分区。
#### 缺点:REST
* 由于 REST 专注于暴露数据,如果资源不是自然组织或无法通过简单的层级结构访问,它可能不是一个好的选择。例如,返回过去一小时内与特定事件集匹配的所有更新记录并不容易通过路径来表达。使用 REST,可能会通过 URI 路径、查询参数以及可能的请求体的组合来实现。
* REST 通常依赖于少数几个动词(GET、POST、PUT、DELETE 和 PATCH),这有时并不适合你的用例。例如,将过期文档移动到归档文件夹可能无法清晰地适应这些动词。
* 获取具有嵌套层级的复杂资源需要在客户端和服务器之间进行多次往返才能渲染单个视图,例如获取博客条目的内容及该条目的评论。对于在多变网络条件下运行的移动应用程序,这些多次往返是非常不可取的。
* 随着时间的推移,API 响应中可能会添加更多字段,较旧的客户端将接收所有新数据字段,即使是它们不需要的字段,因此这会导致负载大小膨胀并导致更大的延迟。
### 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 |
来源:Do you really know why you prefer REST over RPC
#### 来源及延伸阅读:REST 与 RPC
* [Do you really know why you prefer REST over RPC](https://apihandyman.io/do-you-really-know-why-you-prefer-rest-over-rpc/)
* [When are RPC-ish approaches more appropriate than REST?](http://programmers.stackexchange.com/a/181186)
* [REST vs JSON-RPC](http://stackoverflow.com/questions/15056878/rest-vs-json-rpc)
* [Debunking the myths of RPC and REST](https://web.archive.org/web/20170608193645/http://etherealbits.com/2012/12/debunking-the-myths-of-rpc-rest/)
* [What are the drawbacks of using REST](https://www.quora.com/What-are-the-drawbacks-of-using-RESTful-APIs)
* [Crack the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
* [Thrift](https://code.facebook.com/posts/1468950976659943/)
* [Why REST for internal use and not RPC](http://arstechnica.com/civis/viewtopic.php?t=1190508)
## 安全
本节内容有待更新。请考虑[贡献](#contributing)!
安全是一个广泛的话题。除非你有丰富的经验、安全背景,或者正在申请需要安全知识的职位,否则你可能不需要了解太多基础知识以外的内容:
* 对传输中和静态数据进行加密。
* 对所有用户输入或任何暴露给用户的输入参数进行清理,以防止 [XSS](https://en.wikipedia.org/wiki/Cross-site_scripting) 和 [SQL 注入](https://en.wikipedia.org/wiki/SQL_injection)。
* 使用参数化查询来防止 SQL 注入。
* 使用[最小权限](https://en.wikipedia.org/wiki/Principle_of_least_privilege)原则。
### 来源及延伸阅读
* [API security checklist](https://github.com/shieldfy/API-Security-Checklist)
* [Security guide for developers](https://github.com/FallibleInc/security-guide-for-developers)
* [OWASP top ten](https://www.owasp.org/index.php/OWASP_Top_Ten_Cheat_Sheet)
## 附录
有时你会被要求进行“信封背面”估算。例如,你可能需要确定从磁盘生成 100 个图像缩略图需要多长时间,或者一个数据结构将占用多少内存。**2 的幂次表**和**每个程序员都应该知道的延迟数字**是方便的参考。
### 2 的幂次表
```
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
```
#### 来源及延伸阅读
* [Powers of two](https://en.wikipedia.org/wiki/Power_of_two)
### 每个程序员都应该知道的延迟数字
```
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
```
基于上述数字的便捷指标:
* 从 HDD 顺序读取的速度为 30 MB/s
* 从 1 Gbps Ethernet 顺序读取的速度为 100 MB/s
* 从 SSD 顺序读取的速度为 1 GB/s
* 从主内存顺序读取的速度为 4 GB/s
* 每秒 6-7 次全球往返
* 数据中心内每秒 2,000 次往返
#### 延迟数字可视化

#### 来源及延伸阅读
* [Latency numbers every programmer should know - 1](https://gist.github.com/jboner/2841832)
* [Latency numbers every programmer should know - 2](https://gist.github.com/hellerbarde/2843375)
* [Designs, lessons, and advice from building large distributed systems](http://www.cs.cornell.edu/projects/ladis2009/talks/dean-keynote-ladis2009.pdf)
* [Software Engineering Advice from Building Large-Scale Distributed Systems](https://static.googleusercontent.com/media/research.google.com/en//people/jeff/stanford-295-talk.pdf)
### 其他系统设计面试题
| 问题 | 参考资料 |
|---|---|
| 设计像 Dropbox 这样的文件同步服务 | [youtube.com](https://www.youtube.com/watch?v=PE4gwstWhmc) |
| 设计像 Google 这样的搜索引擎 | [queue.acm.org](http://queue.acm.org/detail.cfm?id=988407)
[stackexchange.com](http://programmers.stackexchange.com/questions/38324/interview-question-how-would-you-implement-google-search)
[ardendertat.com](http://www.ardendertat.com/2012/01/11/implementing-search-engines/)
[stanford.edu](http://infolab.stanford.edu/~backrub/google.html) |
| 设计像 Google 这样的可扩展网页爬虫 | [quora.com](https://www.quora.com/How-can-I-build-a-web-crawler-from-scratch) |
| 设计 Google docs | [code.google.com](https://code.google.com/p/google-mobwrite/)
[neil.fraser.name](https://neil.fraser.name/writing/sync/) |
| 设计像 Redis 这样的键值存储 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| 设计像 Memcached 这样的缓存系统 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| 设计像 Amazon 那样的推荐系统 | [hulu.com](https://web.archive.org/web/20170406065247/http://tech.hulu.com/blog/2011/09/19/recommendation-system.html)
[ijcai13.org](http://ijcai13.org/files/tutorial_slides/td3.pdf) |
| 设计像 Bitly 这样的短网址系统 | [n00tc0d3r.blogspot.com](http://n00tc0d3r.blogspot.com/) |
| 设计像 WhatsApp 这样的聊天应用 | [highscalability.com](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html)
| 设计像 Instagram 这样的图片分享系统 | [highscalability.com](http://highscalability.com/flickr-architecture)
[highscalability.com](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html) |
| 设计 Facebook 新闻推送功能 | [quora.com](http://www.quora.com/What-are-best-practices-for-building-something-like-a-News-Feed)
[quora.com](http://www.quora.com/Activity-Streams/What-are-the-scaling-issues-to-keep-in-mind-while-developing-a-social-network-feed)
[slideshare.net](http://www.slideshare.net/danmckinley/etsy-activity-feeds-architecture) |
| 设计 Facebook 时间线功能 | [facebook.com](https://www.facebook.com/note.php?note_id=10150468255628920)
[highscalability.com](http://highscalability.com/blog/2012/1/23/facebook-timeline-brought-to-you-by-the-power-of-denormaliza.html) |
| 设计 Facebook 聊天功能 | [erlang-factory.com](http://www.erlang-factory.com/upload/presentations/31/EugeneLetuchy-ErlangatFacebook.pdf)
[facebook.com](https://www.facebook.com/note.php?note_id=14218138919&id=9445547199&index=0) |
| 设计像 Facebook 那样的图谱搜索功能 | [facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-building-out-the-infrastructure-for-graph-search/10151347573598920)
[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-indexing-and-ranking-in-graph-search/10151361720763920)
[facebook.com](https://www.facebook.com/notes/facebook-engineering/under-the-hood-the-natural-language-interface-of-graph-search/10151432733048920) |
| 设计像 CloudFlare 这样的内容分发网络 | [figshare.com](https://figshare.com/articles/Globally_distributed_content_delivery/6605972) |
| 设计像 Twitter 那样的热门话题系统 | [michael-noll.com](http://www.michael-noll.com/blog/2013/01/18/implementing-real-time-trending-topics-in-storm/)
[snikolov .wordpress.com](http://snikolov.wordpress.com/2012/11/14/early-detection-of-twitter-trends/) |
| 设计随机 ID 生成系统 | [blog.twitter.com](https://blog.twitter.com/2010/announcing-snowflake)
[github.com](https://github.com/twitter/snowflake/) |
| 返回时间间隔内的前 k 个请求 | [cs.ucsb.edu](https://www.cs.ucsb.edu/sites/default/files/documents/2005-23.pdf)
[wpi.edu](http://davis.wpi.edu/xmdv/docs/EDBT11-diyang.pdf) |
| 设计一个从多个数据中心提供服务的系统 | [highscalability.com](http://highscalability.com/blog/2009/8/24/how-google-serves-data-from-multiple-datacenters.html) |
| 设计在线多人卡牌游戏 | [indieflashblog.com](https://web.archive.org/web/20180929181117/http://www.indieflashblog.com/how-to-create-an-asynchronous-multiplayer-game.html)
[buildnewgames.com](http://buildnewgames.com/real-time-multiplayer/) |
| 设计垃圾回收系统 | [stuffwithstuff.com](http://journal.stuffwithstuff.com/2013/12/08/babys-first-garbage-collector/)
[washington.edu](http://courses.cs.washington.edu/courses/csep521/07wi/prj/rick.pdf) |
| 设计 API 速率限制器 | [https://stripe.com/blog/](https://stripe.com/blog/rate-limiters) |
| 设计证券交易所(如 NASDAQ 或 Binance) | [Jane Street](https://youtu.be/b1e4t2k2KJY)
[Golang Implementation](https://around25.com/blog/building-a-trading-engine-for-a-crypto-exchange/)
[Go Implementation](http://bhomnick.net/building-a-simple-limit-order-in-go/) |
| 添加系统设计问题 | [贡献](#contributing) |
### 现实世界架构
Source: Twitter timelines at scale
**对于以下文章,不要关注细枝末节,而是:**
* 识别这些文章中共同的原理、通用技术和模式
* 研究每个组件解决了什么问题,在哪里适用,在哪里不适用
* 回顾经验教训
|类型 | 系统 | 参考资料 |
|---|---|---|
| 数据处理 | **MapReduce** - Google 的分布式数据处理 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/mapreduce-osdi04.pdf) |
| 数据处理 | **Spark** - Databricks 的分布式数据处理 | [slideshare.net](http://www.slideshare.net/AGrishchenko/apache-spark-architecture) |
| 数据处理 | **Storm** - Twitter 的分布式数据处理 | [slideshare.net](http://www.slideshare.net/previa/storm-16094009) |
| | | |
| 数据存储 | **Bigtable** - Google 的分布式列式数据库 | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/chang06bigtable.pdf) |
| 数据存储 | **HBase** - Bigtable 的开源实现 | [slideshare.net](http://www.slideshare.net/alexbaranau/intro-to-hbase) |
| 数据存储 | **Cassandra** - Facebook 的分布式列式数据库 | [slideshare.net](http://www.slideshare.net/planetcassandra/cassandra-introduction-features-30103666)
| 数据存储 | **DynamoDB** - Amazon 的面向文档数据库 | [harvard.edu](http://www.read.seas.harvard.edu/~kohler/class/cs239-w08/decandia07dynamo.pdf) |
| 数据存储 | **MongoDB** - 面向文档数据库 | [slideshare.net](http://www.slideshare.net/mdirolf/introduction-to-mongodb) |
| 数据存储 | **Spanner** - Google 的全球分布式数据库 | [research.google.com](http://research.google.com/archive/spanner-osdi2012.pdf) |
| 数据存储 | **Memcached** - 分布式内存缓存系统 | [slideshare.net](http://www.slideshare.net/oemebamo/introduction-to-memcached) |
| 数据存储 | **Redis** - 具有持久化和值类型的分布式内存缓存系统 | [slideshare.net](http://www.slideshare.net/dvirsky/introduction-to-redis) |
| | | |
| 文件系统 | **Google File System (GFS)** - 分布式文件系统 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/zh-CN/us/archive/gfs-sosp2003.pdf) |
| 文件系统 | **Hadoop File System (HDFS)** - GFS 的开源实现 | [apache.org](http://hadoop.apache.org/docs/stable/hadoop-project-dist/hadoop-hdfs/HdfsDesign.html) |
| | | |
| 杂项 | **Chubby** - Google 面向松耦合分布式系统的锁服务 | [research.google.com](http://static.googleusercontent.com/external_content/untrusted_dlcp/research.google.com/en/us/archive/chubby-osdi06.pdf) |
| 杂项 | **Dapper** - 分布式系统追踪基础设施 | [research.google.com](http://static.googleusercontent.com/media/research.google.com/en//pubs/archive/36356.pdf)
| 杂项 | **Kafka** - LinkedIn 的发布/订阅消息队列 | [slideshare.net](http://www.slideshare.net/mumrah/kafka-talk-tri-hug) |
| 杂项 | **Zookeeper** - 实现同步的集中式基础设施和服务 | [slideshare.net](http://www.slideshare.net/sauravhaloi/introduction-to-apache-zookeeper) |
| | 添加架构 | [贡献](#contributing) |
### 公司架构
| 公司 | 参考资料 |
|---|---|
| Amazon | [Amazon architecture](http://highscalability.com/amazon-architecture) |
| Cinchcast | [Producing 1,500 hours of audio every day](http://highscalability.com/blog/2012/7/16/cinchcast-architecture-producing-1500-hours-of-audio-every-d.html) |
| DataSift | [Realtime datamining At 120,000 tweets per second](http://highscalability.com/blog/2011/11/29/datasift-architecture-realtime-datamining-at-120000-tweets-p.html) |
| Dropbox | [How we've scaled Dropbox](
) |
| ESPN | [Operating At 100,000 duh nuh nuhs per second](http://highscalability.com/blog/2013/11/4/espns-architecture-at-scale-operating-at-100000-duh-nuh-nuhs.html) |
| Google | [Google architecture](http://highscalability.com/google-architecture) |
| Instagram | [14 million users, terabytes of photos](http://highscalability.com/blog/2011/12/6/instagram-architecture-14-million-users-terabytes-of-photos.html)
[What powers Instagram](http://instagram-engineering.tumblr.com/post/13649370142/what-powers-instagram-hundreds-of-instances) |
| Justin.tv | [Justin.Tv's live video broadcasting architecture](http://highscalability.com/blog/2010/3/16/justintvs-live-video-broadcasting-architecture.html) |
| Facebook | [Scaling memcached at Facebook](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/key-value/fb-memcached-nsdi-2013.pdf)
[TAO: Facebook’s distributed data store for the social graph](https://cs.uwaterloo.ca/~brecht/courses/854-Emerging-2014/readings/data-store/tao-facebook-distributed-datastore-atc-2013.pdf)
[Facebook’s photo storage](https://www.usenix.org/legacy/event/osdi10/tech/full_papers/Beaver.pdf)
[How Facebook Live Streams To 800,000 Simultaneous Viewers](http://highscalability.com/blog/2016/6/27/how-facebook-live-streams-to-800000-simultaneous-viewers.html) |
| Flickr | [Flickr architecture](http://highscalability.com/flickr-architecture) |
| Mailbox | [From 0 to one million users in 6 weeks](http://highscalability.com/blog/2013/6/18/scaling-mailbox-from-0-to-one-million-users-in-6-weeks-and-1.html) |
| Netflix | [A 360 Degree View Of The Entire Netflix Stack](http://highscalability.com/blog/2015/11/9/a-360-degree-view-of-the-entire-netflix-stack.html)
[Netflix: What Happens When You Press Play?](http://highscalability.com/blog/2017/12/11/netflix-what-happens-when-you-press-play.html) |
| Pinterest | [From 0 To 10s of billions of page views a month](http://highscalability.com/blog/2013/4/15/scaling-pinterest-from-0-to-10s-of-billions-of-page-views-a.html)
[18 million visitors, 10x growth, 12 employees](http://highscalability.com/blog/2012/5/21/pinterest-architecture-update-18-million-visitors-10x-growth.html) |
| Playfish | [50 million monthly users and growing](http://highscalability.com/blog/2010/9/21/playfishs-social-gaming-architecture-50-million-monthly-user.html) |
| PlentyOfFish | [PlentyOfFish architecture](http://highscalability.com/plentyoffish-architecture) |
| Salesforce | [How they handle 1.3 billion transactions a day](http://highscalability.com/blog/2013/9/23/salesforce-architecture-how-they-handle-13-billion-transacti.html) |
| Stack Overflow | [Stack Overflow architecture](http://highscalability.com/blog/2009/8/5/stack-overflow-architecture.html) |
| TripAdvisor | [40M visitors, 200M dynamic page views, 30TB data](http://highscalability.com/blog/2011/6/27/tripadvisor-architecture-40m-visitors-200m-dynamic-page-view.html) |
| Tumblr | [15 billion page views a month](http://highscalability.com/blog/2012/2/13/tumblr-architecture-15-billion-page-views-a-month-and-harder.html) |
| Twitter | [Making Twitter 10000 percent faster](http://highscalability.com/scaling-twitter-making-twitter-10000-percent-faster)
[Storing 250 million tweets a day using MySQL](http://highscalability.com/blog/2011/12/19/how-twitter-stores-250-million-tweets-a-day-using-mysql.html)
[150M active users, 300K QPS, a 22 MB/S firehose](http://highscalability.com/blog/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html)
[Timelines at scale](https://www.infoq.com/presentations/Twitter-Timeline-Scalability)
[Big and small data at Twitter](https://www.youtube.com/watch?v=5cKTP36HVgI)
[Operations at Twitter: scaling beyond 100 million users](https://www.youtube.com/watch?v=z8LU0Cj6BOU)
[How Twitter Handles 3,000 Images Per Second](http://highscalability.com/blog/2016/4/20/how-twitter-handles-3000-images-per-second.html) |
| Uber | [How Uber scales their real-time market platform](http://highscalability.com/blog/2015/9/14/how-uber-scales-their-real-time-market-platform.html)
[Lessons Learned From Scaling Uber To 2000 Engineers, 1000 Services, And 8000 Git Repositories](http://highscalability.com/blog/2016/10/12/lessons-learned-from-scaling-uber-to-2000-engineers-1000-ser.html) |
| WhatsApp | [The WhatsApp architecture Facebook bought for $19 billion](http://highscalability.com/blog/2014/2/26/the-whatsapp-architecture-facebook-bought-for-19-billion.html) |
| YouTube | [YouTube scalability](https://www.youtube.com/watch?v=w5WVu624fY8)
[YouTube architecture](http://highscalability.com/youtube-architecture) |
### 公司技术博客
* [Airbnb Engineering](http://nerds.airbnb.com/)
* [Atlassian Developers](https://developer.atlassian.com/blog/)
* [AWS Blog](https://aws.amazon.com/blogs/aws/)
* [Bitly Engineering Blog](http://word.bitly.com/)
* [Box Blogs](https://blog.box.com/blog/category/engineering)
* [Cloudera Developer Blog](http://blog.cloudera.com/)
* [Dropbox Tech Blog](https://tech.dropbox.com/)
* [Engineering at Quora](https://www.quora.com/q/quoraengineering)
* [Ebay Tech Blog](http://www.ebaytechblog.com/)
* [Evernote Tech Blog](https://blog.evernote.com/tech/)
* [Etsy Code as Craft](http://codeascraft.com/)
* [Facebook Engineering](https://www.facebook.com/Engineering)
* [Flickr Code](http://code.flickr.net/)
* [Foursquare Engineering Blog](http://engineering.foursquare.com/)
* [GitHub Engineering Blog](https://github.blog/category/engineering)
* [Google Research Blog](http://googleresearch.blogspot.com/)
* [Groupon Engineering Blog](https://engineering.groupon.com/)
* [Heroku Engineering Blog](https://engineering.heroku.com/)
* [Hubspot Engineering Blog](http://product.hubspot.com/blog/topic/engineering)
* [High Scalability](http://highscalability.com/)
* [Instagram Engineering](http://instagram-engineering.tumblr.com/)
* [Intel Software Blog](https://software.intel.com/en-us/blogs/)
* [Jane Street Tech Blog](https://blogs.janestreet.com/category/ocaml/)
* [LinkedIn Engineering](http://engineering.linkedin.com/blog)
* [Microsoft Engineering](https://engineering.microsoft.com/)
* [Microsoft Python Engineering](https://blogs.msdn.microsoft.com/pythonengineering/)
* [Netflix Tech Blog](http://techblog.netflix.com/)
* [Paypal Developer Blog](https://medium.com/paypal-engineering)
* [Pinterest Engineering Blog](https://medium.com/@Pinterest_Engineering)
* [Reddit Blog](http://www.redditblog.com/)
* [Salesforce Engineering Blog](https://developer.salesforce.com/blogs/engineering/)
* [Slack Engineering Blog](https://slack.engineering/)
* [Spotify Labs](https://labs.spotify.com/)
* [Stripe Engineering Blog](https://stripe.com/blog/engineering)
* [Twilio Engineering Blog](http://www.twilio.com/engineering)
* [Twitter Engineering](https://blog.twitter.com/engineering/)
* [Uber Engineering Blog](http://eng.uber.com/)
* [Yahoo Engineering Blog](http://yahooeng.tumblr.com/)
* [Yelp Engineering Blog](http://engineeringblog.yelp.com/)
* [Zynga Engineering Blog](https://www.zynga.com/blogs/engineering)
#### 来源及延伸阅读
想添加博客?为了避免重复工作,请考虑将你的公司博客添加到以下仓库:
* [kilimchoi/engineering-blogs](https://github.com/kilimchoi/engineering-blogs)
## 开发中
有兴趣添加章节或帮助完成正在进行的内容?[贡献](#contributing)!
* 使用 MapReduce 进行分布式计算
* 一致性哈希
* Scatter gather
* [贡献](#contributing)
## 致谢
致谢和来源贯穿本仓库。
特别感谢:
* [Hired in tech](http://www.hiredintech.com/system-design/the-system-design-process/)
* [Cracking the coding interview](https://www.amazon.com/dp/0984782850/)
* [High scalability](http://highscalability.com/)
* [checkcheckzz/system-design-interview](https://github.com/checkcheckzz/system-design-interview)
* [shashank88/system_design](https://github.com/shashank88/system_design)
* [mmcgrana/services-engineering](https://github.com/mmcgrana/services-engineering)
* [System design cheat sheet](https://gist.github.com/vasanthk/485d1c25737e8e72759f)
* [A distributed systems reading list](http://dancres.github.io/Pages/)
* [Cracking the system design interview](http://www.puncsky.com/blog/2016-02-13-crack-the-system-design-interview)
## 联系方式
随时联系我讨论任何问题、疑问或评论。
我的联系方式可以在我的 [GitHub 页面](https://github.com/donnemartin)上找到。
## 许可证
*我根据开源许可证在本仓库中向您提供代码和资源。因为这是我的个人仓库,您获得的我的代码和资源许可证来自我,而不是我的雇主 (Facebook)。*
```
Copyright 2017 Donne Martin
Creative Commons Attribution 4.0 International License (CC BY 4.0)
http://creativecommons.org/licenses/by/4.0/
```