存储架构

Twitter的Timeline是怎样服务数亿用户的?

微信扫一扫,分享到朋友圈

Twitter的Timeline是怎样服务数亿用户的?
0

和传统的网站相比,社交网站的扩展是非常难的工程问题。有几方面的原因:

  • 社交网站用户的活跃度更高,在同一时刻,很大比例的用户处于活跃状态。与这些用户相关的数据也处于活跃状态,因此大量数据必须保存在缓存中,以便加快存取。
  • 数据难以切分,因为社交网络的用户是相互连接的。对于普通的网站,可以使用简单的一致性hash算法把数据分散到不同节点上,但是社交网络没办法做这种简单处理。

Twitter的时间线(Timeline)服务就充分展示了社交网络应用的复杂性。时间线展示了社交网络用户发布的内容,不同社交网络对于时间线的读、写请求,有不同的处理方式。Facebook在用户发布状态时,只是把数据写入数据库;在用户读取时间线的时候,从数据库中读取所有好友的状态更新,构造时间线。但是Twitter的做法不同,会存储并维护每个用户独立的时间线,在用户发推文的时候,Twitter会把这条推文的ID写入所有关注者的时间线中;在用户读取的时候,可以快速获得自己的时间线。下面分析一下Twitter的时间线服务。

Twitter

Twitter逐步从一个简单三层架构的Ruby On Rails应用成长为一个服务驱动的庞大网站。当前拥有数亿活跃用户,获取时间线的请求达到30万QPS,每天有多达4亿条推文在系统里流动。 从Lady Gaga发出一条推文,到她3100万粉丝全部收到需要5分钟时间。

  • Twitter不再是一个Web应用,它致力通过一系列的API,成为整个地球最大的实时消息总线。
  • Twitter系统以消费为主,而不是生产为主。时间线读请求达到30万QPS,而写请求只有6000QPS左右。
  • 有庞大粉丝群体的用户越来越常见。这样的用户发一条推文扩散到所有粉丝有可能很慢。Twitter试图在5秒内完成,但是并不是总能达成这个目标,尤其当名人之间互发推文的时候。
  • 每个用户的主页时间线以列表形式保存在Redis集群中,最多可存储800条推文。

Twitter的挑战

  • 数亿用户,时间线服务超过30万QPS(包括主页和搜索)。人们在Twitter不停创造内容。Twitter的任务就是把内容整合并向粉丝推送出去。
  • 曾尝试采用select语句查询来构造时间线,在用户超过一定数量时,已经不可行。
  • 解决方案是写操作的扩散(fanout)。当推文到达的时候,系统把推文写到多个位置上。这可以让读操作更加便捷快速。读操作本身不会做任何计算。由于所有工作都由写操作完成,写操作的速率低于读操作,大概在4000QPS左右。
  • 真正的挑战是实时性的要求。Twitter的目标是让推文消息在5秒钟之内能到达用户。消息推送的目的地包括时间线集群(内存中)、移动APP推送、邮件、短信等等。Twitter是单用户SMS发送最多的应用。

技术方案

  • Twitter的信息发布有两种方式:
    • 拉取:主页时间线和查询、搜索API是基于拉取实现的。
    • 推送:Twitter运行着一个最大的实时事件推送系统,速率达到22MB/S。客户端向Twitter推送服务建立一个socket连接,twitter会在150ms内完成推文的推送。在任何时刻,有100万个连接连在推送服务上。TwitterDeck应用也是使用推送服务来接受推文的。

  • 基于拉取的时间线:

时间线有两种:主页时间线(关注的用户发布的所有推文)和用户时间线(用户发送的所有推文),时间线的构造涉及一些业务逻辑,比如当前未关注的用户的回复会被过滤掉。

当推文通过写接口到达的时候,它经过负载均衡和TFE(Twitter前端服务),执行一些业务逻辑。然后,就开始执行fanout过程:推文先存储在一个庞大的Redis集群中,每条推文会在3个不同机器上有备份;然后fanout查询社交关系数据库(Flock),Flock维护着用户间互相关注的关系图;Flock返回推文接受者的列表,然后fanout开始循环把推文写到这些人的时间线中(时间线使用Redis的List存储)。假设你有2万粉丝,fanout服务会查找你的2万粉丝的时间线所在的位置,然后把推文ID、发送方ID以及几个标志位插入到这些时间线中。所以你发一条推文,会有2万个插入操作。每个人的home时间线最多有800条。活跃用户(过去30天内有登录的用户)的时间线存储在内存中,以减少响应时间。如果一个用户的时间线不在内存中,访问时会触发一个重建的过程:先查询社交关系数据库,找到你关注的人,然后查询每个人的推文,把构造好的时间线重新写入Redis。推文的磁盘存储使用的是MySQL。

处理读时间线请求时,只要找到相应用户的时间线在集群中的位置,就可以迅速返回。提高读操作效率的代价就是写操作(fanout)需要的时间长一些。因为时间线的数据结构中只包含ID,在读操作返回给用户之前,需要填充推文的内容,向T-bird(推文的存储服务)发送一条multiget的命令即可获得所有推文。在时间线返回之前也需要做一些过滤操作,比如在法国,和纳粹相关的内容会被过滤掉。

写到所有Redis集群是一个O(N)的过程。对于N很大的情况,比如Taylor Swift或者奥巴马这样的名人发布一条推文,需要有上千万的fanout操作;当两个名人互发推文,这个延迟时间就会更长。对于这种情况,Twitter考虑了读路径和写路径的平衡:对于粉丝众多的用户,他们的推文不是在写入时fanout,而是在粉丝读取时查询、并入时间线。

时间线的读取是一个O(1)操作,仅需几十毫秒。Twitter对于读操作做这样的优化,是因为Twitter偏重于信息消费,而不是信息生产。

  • 搜索和时间线相反,所有的计算都发生在读路径上。

当推文进入系统时,Ingester对推文做词法分析,根据索引的种类,把推文发送到EarlyBird(一个自定制的Lucene),索引是存储在内存中的。和fanout不同,推文在Early bird只存储一份。在读取搜索结果时,Blender负责完成搜索时间线的构造,它会查询所有的EarlyBird分片,把返回的结果合并、排序并返回。

Twitter的Discovery(发现服务)其实是基于系统对你的了解(你的关注、点击等等)定制的一个特殊搜索。

对于写操作的时间复杂度是O(1),但是读操作是O(N)。但是因为索引保存在内存中,搜索还是比较快的,响应时间在几百毫秒的级别。

参考资料

http:// highscalability.com/blo g/2013/7/8/the-architecture-twitter-uses-to-deal-with-150m-active-users.html

http:// highscalability.com/blo g/2009/10/13/why-are-facebook-digg-and-twitter-so-hard-to-scale.html

Timelines at Scale

阅读原文...


Avatar

Grio and Angular 2: Are They Ready for Each Other?

上一篇

"Perl 6: What Programming In The Future Is Like?" (Lightning Talk Slides and Video)

下一篇

您也可能喜欢

评论已经被关闭。

插入图片
Twitter的Timeline是怎样服务数亿用户的?

长按储存图像,分享给朋友