设计推特时间轴与搜索功能 注意:这个文档中的链接会直接指向系统设计主题索引中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。 设计 Facebook 的 feed 与设计 Facebook 搜索与此为同一类型问题。 第一步:简述用例与约束条件 搜集需求与问题的范围。 提出问题来明确用例与约束条件。 讨论假设。 我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
注意:这个文档中的链接会直接指向系统设计主题索引中的有关部分,以避免重复的内容。你可以参考链接的相关内容,来了解其总的要点、方案的权衡取舍以及可选的替代方案。
设计 Facebook 的 feed 与设计 Facebook 搜索与此为同一类型问题。
搜集需求与问题的范围。
提出问题来明确用例与约束条件。
讨论假设。
我们将在没有面试官明确说明问题的情况下,自己定义一些用例以及限制条件。
普遍情况
时间轴功能
搜索功能
如果你需要进行粗略的用量计算,请向你的面试官说明。
tweet_id - 8 字节user_id - 32 字节text - 140 字节media - 平均 10 KB便利换算指南:
列出所有重要组件以规划概要设计。

深入每个核心组件的细节。
我们可以将用户自己发表的推特存储在关系数据库中。我们也可以讨论一下究竟是用 SQL 还是用 NoSQL。
构建用户主页时间轴(查看关注用户的活动)以及推送推特是件麻烦事。将特推传播给所有关注者(每秒约递送 6 万条推特)这一操作有可能会使传统的关系数据库超负载。因此,我们可以使用 NoSQL 数据库或内存数据库之类的更快的数据存储方式。从内存读取 1 MB 连续数据大约要花 250 微秒,而从 SSD 读取同样大小的数据要花费 4 倍的时间,从机械硬盘读取需要花费 80 倍以上的时间。1
我们可以将照片、视频之类的媒体存储于对象存储中。
向你的面试官告知你准备写多少代码。
如果我们用 Redis 作为内存缓存,那可以用 Redis 原生的 list 作为其数据结构。结构如下:
tweet n+2 tweet n+1 tweet n | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | | tweet_id user_id meta | tweet_id user_id meta | tweet_id user_id meta |
新发布的推特将被存储在对应用户(关注且活跃的用户)的主页时间轴的内存缓存中。
我们可以调用一个公共的 REST API:
$ curl -X POST --data '{ "user_id": "123", "auth_token": "ABC123", \ "status": "hello world!", "media_ids": "ABC987" }' \ https://twitter.com/api/v1/tweet
返回:
{ "created_at": "Wed Sep 05 00:37:15 +0000 2012", "status": "hello world!", "tweet_id": "987", "user_id": "123", ... }
而对于服务器内部的通信,我们可以使用 RPC。
REST API:
$ curl https://twitter.com/api/v1/home_timeline?user_id=123
返回:
{ "user_id": "456", "tweet_id": "123", "status": "foo" }, { "user_id": "789", "tweet_id": "456", "status": "bar" }, { "user_id": "789", "tweet_id": "579", "status": "baz" },
REST API 与前面的主页时间轴类似,区别只在于取出的推特是由用户自己发送而不是关注人发送。
REST API:
$ curl https://twitter.com/api/v1/search?query=hello+world
返回结果与前面的主页时间轴类似,只不过返回的是符合查询条件的推特。
根据限制条件,找到并解决瓶颈。

重要提示:不要从最初设计直接跳到最终设计中!
现在你要 1) 基准测试、负载测试。2) 分析、描述性能瓶颈。3) 在解决瓶颈问题的同时,评估替代方案、权衡利弊。4) 重复以上步骤。请阅读「设计一个系统,并将其扩大到为数以百万计的 AWS 用户服务」 来了解如何逐步扩大初始设计。
讨论初始设计可能遇到的瓶颈及相关解决方案是很重要的。例如加上一个配置多台 Web 服务器的负载均衡器是否能够解决问题?CDN呢?主从复制呢?它们各自的替代方案和需要权衡的利弊又有什么呢?
我们将会介绍一些组件来完成设计,并解决架构扩张问题。内置的负载均衡器将不做讨论以节省篇幅。
为了避免重复讨论,请参考系统设计主题索引相关部分来了解其要点、方案的权衡取舍以及可选的替代方案。
消息输出服务有可能成为性能瓶颈。那些有着百万数量关注着的用户可能发一条推特就需要好几分钟才能完成消息输出进程。这有可能使 @回复 这种推特时出现竞争条件,因此需要根据服务时间对此推特进行重排序来降低影响。
我们还可以避免从高关注量的用户输出推特。相反,我们可以通过搜索来找到高关注量用户的推特,并将搜索结果与用户的主页时间轴合并,再根据时间对其进行排序。
此外,还可以通过以下内容进行优化:
我们还可以考虑优化 SQL 数据库 来解决一些瓶颈问题。
内存缓存能减小一些数据库的负载,靠 SQL Read 副本已经足够处理缓存未命中情况。我们还可以考虑使用一些额外的 SQL 性能拓展技术。
高容量的写入将淹没单个的 SQL 写主从模式,因此需要更多的拓展技术。
我们也可以考虑将一些数据移至 NoSQL 数据库。
是否深入这些额外的主题,取决于你的问题范围和剩下的时间。
请参阅「安全」一章。
请参阅「每个程序员都应该知道的延迟数」。
Note: This document links directly to relevant areas found in the system design topics to avoid duplication. Refer to the linked content for general talking points, tradeoffs, and alternatives.
Design the Facebook feed and Design Facebook search are similar questions.
Gather requirements and scope the problem.
Ask questions to clarify use cases and constraints.
Discuss assumptions.
Without an interviewer to address clarifying questions, we'll define some use cases and constraints.
General
Timeline
Search
Clarify with your interviewer if you should run back-of-the-envelope usage calculations.
tweet_id - 8 bytesuser_id - 32 bytestext - 140 bytesmedia - 10 KB averageHandy conversion guide:
Outline a high level design with all important components.

Dive into details for each core component.
We could store the user's own tweets to populate the user timeline (activity from the user) in a relational database. We should discuss the use cases and tradeoffs between choosing SQL or NoSQL.
Delivering tweets and building the home timeline (activity from people the user is following) is trickier. Fanning out tweets to all followers (60 thousand tweets delivered on fanout per second) will overload a traditional relational database. We'll probably want to choose a data store with fast writes such as a NoSQL database or Memory Cache. Reading 1 MB sequentially from memory takes about 250 microseconds, while reading from SSD takes 4x and from disk takes 80x longer.1
We could store media such as photos or videos on an Object Store.
Clarify with your interviewer how much code you are expected to write.
If our Memory Cache is Redis, we could use a native Redis list with the following structure:
tweet n+2 tweet n+1 tweet n | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | 8 bytes 8 bytes 1 byte | | tweet_id user_id meta | tweet_id user_id meta | tweet_id user_id meta |
The new tweet would be placed in the Memory Cache, which populates the user's home timeline (activity from people the user is following).
We'll use a public REST API:
$ curl -X POST --data '{ "user_id": "123", "auth_token": "ABC123", \ "status": "hello world!", "media_ids": "ABC987" }' \ https://twitter.com/api/v1/tweet
Response:
{ "created_at": "Wed Sep 05 00:37:15 +0000 2012", "status": "hello world!", "tweet_id": "987", "user_id": "123", ... }
For internal communications, we could use Remote Procedure Calls.
REST API:
$ curl https://twitter.com/api/v1/home_timeline?user_id=123
Response:
{ "user_id": "456", "tweet_id": "123", "status": "foo" }, { "user_id": "789", "tweet_id": "456", "status": "bar" }, { "user_id": "789", "tweet_id": "579", "status": "baz" },
The REST API would be similar to the home timeline, except all tweets would come from the user as opposed to the people the user is following.
REST API:
$ curl https://twitter.com/api/v1/search?query=hello+world
The response would be similar to that of the home timeline, except for tweets matching the given query.
Identify and address bottlenecks, given the constraints.

Important: Do not simply jump right into the final design from the initial design!
State you would 1) Benchmark/Load Test, 2) Profile for bottlenecks 3) address bottlenecks while evaluating alternatives and trade-offs, and 4) repeat. See Design a system that scales to millions of users on AWS as a sample on how to iteratively scale the initial design.
It's important to discuss what bottlenecks you might encounter with the initial design and how you might address each of them. For example, what issues are addressed by adding a Load Balancer with multiple Web Servers? CDN? Master-Slave Replicas? What are the alternatives and Trade-Offs for each?
We'll introduce some components to complete the design and to address scalability issues. Internal load balancers are not shown to reduce clutter.
To avoid repeating discussions, refer to the following system design topics for main talking points, tradeoffs, and alternatives:
The Fanout Service is a potential bottleneck. Twitter users with millions of followers could take several minutes to have their tweets go through the fanout process. This could lead to race conditions with @replies to the tweet, which we could mitigate by re-ordering the tweets at serve time.
We could also avoid fanning out tweets from highly-followed users. Instead, we could search to find tweets for highly-followed users, merge the search results with the user's home timeline results, then re-order the tweets at serve time.
Additional optimizations include:
We'll also want to address the bottleneck with the SQL Database.
Although the Memory Cache should reduce the load on the database, it is unlikely the SQL Read Replicas alone would be enough to handle the cache misses. We'll probably need to employ additional SQL scaling patterns.
The high volume of writes would overwhelm a single SQL Write Master-Slave, also pointing to a need for additional scaling techniques.
We should also consider moving some data to a NoSQL Database.
Additional topics to dive into, depending on the problem scope and time remaining.
Refer to the security section.
See Latency numbers every programmer should know.