目录
Timeline & Search 简介
系统设计中,时间线(timeline)功能是十分常见的,诸如朋友圈,微博,Twitter等社交平台都会涉及到,主要功能就是按时间线看到已经关注的人发送的消息。而搜索功能则是公开社交平台的重要功能。
Pull vs. Push
为了实现时间线的功能,可以采用“推”或者“拉”的手段进行实现。比如某位用户发送了一条消息,那么系统将该消息推送给所有关注者;或者,将消息暂存,等待关注的用户查看的时候,再由用户自己吧消息取出。前者是Push,后者是Pull。
系统读写峰值
以Twitter为例,2012年Twitter拥有1.5亿用户,timeline的QPS为300K。其中写的QPS约为4K。因此写需求的QPS远小于读取的QPS
Push
一条消息之后,如何写入Twitter呢?
推文写好之后,经过前端的负载均衡,会调用写入的API,在经过Fanout服务层,他的作用是通过社交网络查询有哪些人关注了发布者,进而将发的推文插入所有关注者的timeline。这些timeline存储在redis中为的就是高效的查找。同时redis还是精心异地容灾操作,保证高可用性。
timeline的存储结构
timeline基于redis自有的List实现,每个List的元素主要由Tweet ID(8 bytes),User ID(8 bytes),Others(4 bytes)组成;Others用于存储其他的信息,最终一个用户的timeline会有以下的样子,
Tips
- timeline的长度不是无限的,通常为800~1000条,所以一直往下滑之后可能会显示没有更多消息可以查看,这一点和现如今的信息流方式是不一样的。限制大小可以加快读取速度
-
如果用户不活跃,那么他的timeline不会存在redis当中。
-
推文在发布之后也会储存在磁盘中。如果非活跃用户上线之后可以从磁盘中读取并构建timeline。
时间线的读取
读取的时候需要从redis找到自己的timeline读取所有的tweet ID,并组成最终的结果。以上的整个过程,主要时间花费在写数据上(需要把发布信息写入所有关注者的时间线里面),读取数据则比较快(当然也是基于一新的缓存策略)。
Search
在Twitter的发布的基础上,增加了如下的架构:
同样的写API,除了会往时间线那么进行插入数据。写入之前会对推文进行分词和过滤,使得推文变成一个一个的token。然后Earlybird其实就是一个修改版本的lucene。和Fanout那边不同的是,一条推文你可能会保存在很多不同的Redis中(取决于有多少人follow你),而这里每条Twitter记录只需要保存在一个Earlybird中就可以了,当然也Earlybird也会有备份。
而对于Blender的读也就是查询来说就比较麻烦了,你需要搜索所有的index,看有没有你想要查询的数据,然后再进行合并排序,最后再返回查询结果。
TimeLine和Search的时间复杂度对比
对于timeline而言,写入是O(n)时间复杂度,读取的时候是O(1)时间复杂度。但是查询的时候正好相反,写入是O(1)时间复杂度,读取的时候是O(n)时间复杂度。
Hot User的处理
以Tim Cook为例,他有上千万的关注,但是普通人可能只有几百上千的关注。
如果Tim发一条推特,那么需要修改一千多万人的timeline,这些时间是不可忽略的,Fanout的压力也上去了。
这个Fanout的延时的问题并不在于你在Tim发送了一条推文之后多久可以看到这个推文,比如说你在关注者列表比较靠后的位置,那么可能在1分钟之后看到刚发的推文,或者30s之后看到,这并不是什么大问题。真正存在的问题是在于推文显示的错序。
比如说你和小明互相关注,然后但是你们也同时关注了Tim,现在Tim发送了一条推文,小明由于排在关注列表的比较前面,于是在发送推文10s后看到了推文并进行转发。但是你咋关注者列表的比较后面,因此需要过60s才看到推文,但是此时却能够看到小明转的推文,因此造成了错序。
其实一个简单的解决方法就是在时间线上按照TwitterID来做一个排序,只要保证TwitterID是全局有序的就可以了。这样一来就可以轻松解决这个问题。全局有序可以参考之前提到的雪花算法。
但是这些hot user还是会大量使用fanout的资源,要想解决这个问题,可以把时间线和查询结合起来:
对于hot user而言,并不对他们的推文进行Fanout,而只写入查询系统当中。如果之后有关注者想要查看推文的时候,首先构建正常的时间线,然后再查询自己关注的hot user的推文,结合自己的时间线进行合并,这样的话就可以避免hot user带来的问题。上述方法也可以理解为pull和push方法的结合。
总结
在timeline和search的系统中,可以将timeline理解为push操作,search为pull操作。