设计Twitter

设计Twitter
让我们设计一个类似Twitter weibo的社交网络服务。服务的用户将能够发布推文,关注其他人和喜欢的推文。

1.什么是Twitter?

Twitter是一种在线社交网络服务,用户可以在其中发布和阅读140个字符的短消息,称为“ tweets”。已注册的用户可以发布和阅读推文,但未注册的用户只能阅读它们。用户通过其网站界面,SMS或移动应用程序访问Twitter。

2.需求和系统的目标

我们将设计一个具有以下要求的简化版Twitter:
功能要求:

  1. 用户应该能够发布新的推文/微博。
  2. 一个用户应该能够关注其他用户。
  3. 用户应该能够将推文标记为收藏。
  4. 该服务应该能够创建和显示用户的时间轴,该时间轴由来自用户关注的所有人的头条推文组成。
  5. 推文中可以包含照片和视频。

非功能需求:

  1. 我们的服务需要高度可用。
  2. 对于时间轴生成,系统可接受的延迟为200毫秒。
  3. 一致性可能会受到影响(为了可用性);如果用户有一阵子没看到推文,可以接受。

扩展要求

  1. 搜索推文。
  2. 回复推文。
  3. 热门话题–当前热门话题/搜索。
  4. 标记tag其他用户。
  5. 推文通知。
  6. 关注谁?Who to follow? Suggestions?
  7. Moments。

3.容量估算和约束

假设我们有10亿总用户,每天有2亿活跃用户(DAU)。还要假设我们每天有1亿条新推文,平均每个用户关注200人。

每天有多少个收藏夹?: 如果平均每个用户每天收藏五条推文,我们将:

2亿用户* 5个收藏夹=> 1B个收藏夹

我们的系统将生成多少个总的Tweet视图?: 假设一个用户平均每天访问两次其时间轴并访问其他五个人的页面。如果用户在每个页面上看到20条推文,则我们的系统每天将产生28B / 天的总推文视图:

200M DAU ((2 + 5) 20条推文)=> 28B /天

存储估算: 假设每个推文都有140个字符,并且我们需要两个字节来存储一个字符而无需压缩。假设我们需要30个字节来存储带有每条推文的元数据(例如ID,时间戳,用户ID等)。我们需要的总存储量:

100M *(280 + 30)字节=> 30GB /天

五年的存储需求是什么? 我们需要多少存储用户数据,后续数据和收藏夹?我们将把它留给练习。

并非所有tweet都具有媒体,让我们假设平均每5条tweet中就有一张照片,每十分之一中都有一个视频。我们还假设平均照片为200KB,视频为2MB。这将导致我们每天拥有24 TB的新媒体。

(100M / 5张照片* 200KB)+(100M / 10个视频* 2MB)〜= 24TB /天

带宽估计: 由于每天的总入口量为24TB,因此相当于290MB /秒。

请记住,我们每天有28B条Tweet视图。我们必须显示每条推文的照片(如果有照片),但是我们假设用户观看了他们在时间轴上看到的每第三个视频。因此,总出口为:

(28B * 280字节)/文本86400s => 93MB / s
+(28B / 5 * 200KB)/ 86400s的照片=> 13GB / S
+(28B / 10/3 * 2MB)/ 86400s的视频=> 22GB / s
总计〜= 35GB / s

4.系统API

我们可以使用SOAP或REST API来公开我们服务的功能。以下可能是发布新推文的API的定义:

tweet(api_dev_key, tweet_data, tweet_location, user_location, media_ids)

参数
api_dev_key(字符串):注册帐户的API开发人员密钥。除其他外,这将用于根据分配的配额限制用户。
tweet_data(字符串):tweet的文本,通常最多140个字符。
tweet_location(字符串):Tweet所指的可选位置(经度,纬度)。
user_location(字符串):添加推文的用户的可选位置(经度,纬度)。
media_ids(number []):要与Tweet关联的media_id的可选列表。(所有媒体照片,视频等都需要单独上传)。

返回:(字符串)
成功的帖子将返回URL来访问该推文。否则,将返回适当的HTTP错误。

5.高级系统设计

我们需要一个能够有效存储所有新推文的系统,每秒100M / 86400s => 1150条推文,每秒读取28B / 86400s => 325K条推文。从需求中可以明显看出,这将是一个繁重的系统。

在较高的级别上,我们需要多个应用程序服务器来满足所有这些请求,并在它们前面放置负载均衡器以进行流量分配。在后端,我们需要一个高效的数据库,该数据库可以存储所有新推文,并可以支持大量读取。我们还需要一些文件存储空间来存储照片和视频。

尽管我们的预期每日写入负载为1亿,读取负载为280亿条推文。这意味着我们的系统平均每秒将接收大约1160条新推文和32.5万条读取请求。但是,这种流量在一天中的分布会不均匀,但是在高峰时间,我们应该期望至少每秒有数千个写入请求和每秒约100万个读取请求。在设计系统架构时,我们应该牢记这一点。

6.数据库架构编号

我们需要存储有关用户,他们的推文,他们最喜欢的推文以及他们关注的人的数据。

要在用于存储上述架构的SQL数据库和NoSQL数据库之间进行选择,请参阅“设计Instagram ”下的“数据库架构” 。

7.数据分片

由于我们每天都有大量新推文,并且读取负载也非常高,因此我们需要将数据分发到多台计算机上,以便我们可以高效地读取/写入数据。我们有很多选择可以分片数据。

基于UserID的分片:我们可以尝试将用户的所有数据存储在一台服务器上。在存储时,我们可以将UserID传递给哈希函数,该哈希函数会将用户映射到数据库服务器,在该服务器中我们将存储用户的所有推文,收藏夹,关注等。在查询用户的推文/关注/收藏夹时,我们可以问我们的哈希函数,我们在哪里可以找到用户的数据,然后从那里读取数据。这种方法有两个问题:

  1. 如果用户很火怎么办?拥有该用户的服务器上可能有很多查询。这种高负载将影响我们的服务性能。
  2. 随着时间的流逝,与其他用户相比,某些用户最终可能会存储大量推文或拥有大量关注。维持不断增长的用户数据的均匀分布是非常困难的。
    为了从这些情况中恢复,我们必须重新分区/重新分配数据,或者使用一致的哈希。

基于TweetID的分片:我们的哈希函数会将每个TweetID映射到随机服务器,我们将在其中存储该Tweet。要搜索推文,我们必须查询所有服务器,每个服务器将返回一组推文。集中式服务器将汇总这些结果,以将其返回给用户。让我们看一下时间轴生成示例;这是我们的系统生成用户时间轴必须执行的步骤数:

  1. 我们的应用程序(app)服务器将查找用户关注的所有人员。
  2. 应用服务器会将查询发送到所有数据库服务器,以查找来自这些人的推文。
  3. 每个数据库服务器都将找到每个用户的推文,并按新近度对它们进行排序,然后返回最上面的推文。
  4. 应用服务器将合并所有结果,并再次对其进行排序,以将最重要的结果返回给用户。
    这种方法解决了热用户的问题,但是与通过UserID进行分片相比,我们必须查询所有数据库分区以查找用户的tweet,这可能会导致更高的延迟。

通过引入用于在数据库服务器前面存储热推文的缓存,我们可以进一步提高性能。

基于Tweet创建时间的分片:基于创建时间存储Tweet将使我们具有快速获取所有顶级Tweet的优势,而我们只需要查询很少的服务器即可。这里的问题是流量负载将不会分配,例如,在写入时,所有新推文都将发送到一台服务器,而其余服务器将处于空闲状态。类似地,在读取时,与保存旧数据的服务器相比,保存最新数据的服务器将具有非常高的负载。

**如果我们可以结合使用TweetID和Tweet创建时间进行分片怎么办?**如果我们不单独存储推文创建时间,而是使用TweetID来反映这一点,那么我们可以从这两种方法中受益。这样一来,找到最新的推文将很快。为此,我们必须使每个TweetID在我们的系统中通用唯一,并且每个TweetID也应包含一个时间戳。

我们可以为此使用纪元时间。假设我们的TweetID有两个部分:第一部分将代表时期,第二部分将是一个自动递增的序列。因此,要创建一个新的TweetID,我们可以采用当前的纪元时间,并为其添加一个自动递增的数字。我们可以从此TweetID中找出分片号并将其存储在此。

我们的TweetID的大小可能是多少?假设我们的纪元时间从今天开始,接下来的50年我们需要多少位来存储秒数?

86400秒/天* 365(一年中的天数)* 50(年)=> 1.6B

我们将需要31位存储该数字。由于平均而言,我们期望每秒1150条新推文,因此我们可以分配17位来存储自动递增的序列;这将使我们的TweetID长48位。因此,我们每秒可以存储(2 ^ 17 => 130K)新推文。我们可以每秒重置一次自动递增序列。为了实现容错和更好的性能,我们可以有两个数据库服务器为我们生成自动递增密钥,一个数据库生成偶数密钥,另一个数据库生成奇数密钥。

如果我们假设当前的纪元秒为“ 1483228800”,则我们的TweetID将如下所示:

1483228800 000001
1483228800 000002
1483228800 000003
1483228800 000004

如果我们将TweetID的长度设置为64bits(8字节),则可以轻松存储接下来100年的tweet,也可以以毫秒级的粒度存储它们。

在上述方法中,我们仍然必须查询所有服务器以生成时间轴,但是我们的读取(和写入)速度将大大提高。

由于我们没有任何二级索引(在创建时),这将减少我们的写入延迟。
在阅读时,我们不需要过滤创建时间,因为我们的主键中包含了纪元时间。

8.缓存

我们可以为数据库服务器引入缓存,以缓存热推文和用户。我们可以使用像Memcache这样的现成解决方案来存储整个tweet对象。在访问数据库之前,应用程序服务器可以快速检查缓存是否具有所需的tweet。根据客户的使用模式,我们可以确定我们需要多少个缓存服务器。

**哪种缓存替换策略最适合我们的需求?**当缓存已满并且我们想用更新/更热的推文替换推文时,我们将如何选择?对于我们的系统,最近最少使用(LRU)可能是一个合理的策略。根据这项政策,我们会先丢弃最近浏览最少的推文。

**我们如何拥有更智能的缓存?**如果我们遵循80-20规则,那就是20%的推文产生了80%的读取流量,这意味着某些推文非常受欢迎,以至于大多数人都在阅读它们。这表明我们可以尝试从每个分片缓存每日读取量的20%。

**如果我们缓存最新数据怎么办?**我们的服务可以从这种方法中受益。假设我们80%的用户仅在过去三天内看到过推文;我们可以尝试缓存过去三天的所有推文。假设我们有专用的缓存服务器,可以缓存过去三天来自所有用户的所有推文。根据以上估算,我们每天将收到1亿条新推文或30GB新数据(没有照片和视频)。如果我们要存储最近三天的所有推文,则需要不到100GB的内存。这些数据可以很容易地放入一台服务器中,但是我们应该将其复制到多台服务器上,以分配所有读取的流量,以减少缓存服务器的负载。因此,无论何时生成用户的时间轴,我们都可以询问缓存服务器是否具有该用户的所有最新推文。如果是,我们可以简单地从缓存中返回所有数据。如果缓存中没有足够的推文,则必须查询后端服务器以获取该数据。在类似的设计中,我们可以尝试缓存最近三天的照片和视频。

我们的缓存就像一个哈希表,其中“ key”将是“ OwnerID”,“ value”将是一个双向链接列表,其中包含该用户在过去三天内的所有推文。由于我们要首先检索最新数据,因此我们总是可以在链接列表的开头插入新的tweet,这意味着所有较旧的tweet都将位于链接列表的末尾。因此,我们可以从尾部删除推文,以便为更新的推文留出空间。

10.复制和容错

由于我们的系统读量很大,因此每个数据库分区可以有多个辅助数据库服务器。辅助服务器将仅用于读取流量。所有写操作都将首先转到主服务器,然后将其复制到辅助服务器。该方案还将为我们提供容错能力,因为每当主服务器出现故障时,我们都可以故障转移到辅助服务器。

11.负载平衡

我们可以在系统中的三个位置添加负载平衡层:

  1. 在客户端和应用程序服务器之间
  2. 在应用程序服务器和数据库复制服务器之间以及
  3. 在聚合服务器和缓存服务器之间。
    最初,可以采用简单的循环法。在服务器之间平均分配传入请求。该LB易于实现,不会带来任何开销。这种方法的另一个好处是,如果服务器死了,则LB会将其从循环中移出,并停止向该服务器发送任何流量。Round Robin LB的一个问题是它不会考虑服务器负载。如果服务器过载或运行缓慢,则LB不会停止向该服务器发送新请求。为了解决这个问题,

12.监控

监视我们的系统的能力至关重要。我们应该不断收集数据,以便即时了解我们的系统运行情况。我们可以收集以下指标/计数器,以了解我们的服务性能:

  1. 每天/秒的新推文,每天的峰值是多少?
  2. 时间轴交付统计数据,我们的服务每天交付多少条推文。
  3. 用户看到的刷新时间线的平均延迟。
    通过监视这些计数器,我们将了解是否需要更多的复制,负载平衡或缓存。

13.扩展的需求

我们如何提供提要? 从某人关注的人那里获取所有最新推文,并按时间对其进行合并/排序。使用分页来获取/显示推文。仅从某人关注的所有人中获取前N条推文。此N将取决于客户端的视口,因为与Web客户端相比,在移动设备上,我们显示的推文较少。我们还可以缓存下一个热门推文,以加快处理速度。
或者,我们可以预先生成提要以提高效率;

转推:对于数据库中的每个Tweet对象,我们都可以存储原始Tweet的ID,而不在该转推对象上存储任何内容。

热门话题:我们可以在最近N秒钟内缓存最频繁出现的主题标签或搜索查询,并在每M秒钟后继续更新它们。我们可以根据推文或搜索查询或转发或喜欢的频率对热门话题进行排名。我们可以更加重视显示给更多人的主题。

跟随谁?如何给出建议?此功能将提高用户参与度。我们可以建议某个人的朋友。我们可以向下走两到三个级别,以找到名人来寻求建议。我们可以优先考虑拥有更多关注者的人。

由于随时只能提出一些建议,因此请使用机器学习(ML)进行重新排序并重新确定优先级。ML信号可能包括关注度最近增加的人,其他人关注的共同关注者,共同的位置或兴趣爱好等。

moment片刻:获取过去1或2个小时内不同网站的热门新闻,找出相关推文,对其进行优先排序,并使用ML –监督学习或聚类对它们进行分类(新闻,支持,财务,娱乐等)。然后,我们可以将这些文章显示为Moments中的热门话题。

搜索:搜索涉及对推文进行索引,排名和检索。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值