[关闭]
@elibinary 2016-09-10T10:32:30.000000Z 字数 2035 阅读 985

Feed流系统

未分类


这里说的 feed 系统也就是维护 sns 系统、微博等应用中好友圈,新鲜事等内容的系统。比较常见的两种模式就是推和拉了,下面就分别介绍一下推拉模式与其中的利与弊。

就拿微博为例,用户之间有关注和被关注的关系,当一个用户发表一条博文,关注这个用户的所有粉丝都将能够收到这条博文,那么在这个场景下我们来看下各模式细节

此处输入图片的描述

如图所示,所谓的推模式整个流程是这样的:
1. 首先一条博文发出来
2. 把这条博文的所有信息存到db,并进入分发队列
3. 根据本用户的 followers list 把该博文推入所有 followers 的存储里

图中所例每个用户都会维护一个 feeds list ,这个 list 其实就是来 cache 用户各自的首页博文列表,里边包括了自己的和他所关注的所有用户所发的博文。图中实例是用 redis 来存储(此处仅为示例,当然有其他很多种存储方式),其内存储了 feed 的 ID 、发送时间等信息,存储空间不是很大,用户在检索时,直接访问此存储速度很快,性能也非常高。但是这里有一个很大的问题,如果现在有一个用户发了一条博文,而这个用户拥有数十上百万甚至千万的粉丝数,那么第三步推送将会是一个巨大的任务,并且会产生巨大的数据。

此处输入图片的描述
如图所示,拉模式整个流程是这样的:
1. 用户发出一条博文
2. 直接把这条 feed 直接插入 db feeds 表中,这里有两点需要注意下
3. 用户访问时,最新内容直接从 feeds 表中检索

  1. query = 'SELECT id FROM feeds WHERE user_id in (followers) AND created_at > (last_visit_time);'

检索结果会缓存进 user_feeds_cache 中,用户在检索历史的 feeds 内容时能够从这里直接取值而不需要再次检索数据库。同时更新用户的 last_visit_time 。

* 考虑构造发表队列来处理大请求量
* 图中feeds表部分画的比较粗略,由于feeds表部分字段检索频度高,考虑把feed主体与索引内容分离

可以看出,这种模式实现起来比较简单,其主要性能瓶颈可能会出现在每次检索时,如果检索目标的 following 比较多时,会给数据库造成很大的压力,而且 feeds 表的数据量也不会多小,这在查询性能上没有推模式的效率高,在查询这一块要多考虑怎么优化缓存结构来提升性能。

拉+

此处输入图片的描述

该模式是对拉模式的改进,该改进主要体现在 feeds 的存储上。
改进后的存储结构把 feeds 索引表由一个增加到数个,分别存储最近一段时间的、稍长一段近期时间的以及长时期的等。
如图所示,拉+模式整个流程是这样的:
1. 用户发不一条 feed ,这条 feed 将会分别插入到各个分区的 feeds 表中,同时 feed 的详细内容插入 feed_body 表
2. 当有用户拉取 feeds 数据时,会先到 cache 中去找到 last_visit_time ,然后根据该时间确定到哪个 feeds 分区表中检索 feeds 数据
3. 将检索出来的数据 cache 到 user_feeds_cache 中,并更新 last_visit_time,用户拉取历史数据时直接从 redis 中取而不用每次都检索数据库

改进后的拉模式,就能够对经常访问的用户做出极大的优化,因为这些用户的 last_visit_time 都会落在最近也就是最小的 feeds 分区表中,这是查询是非常高效的。对于那些很长时间没有访问的用户,他们在去较长时间的大表中查询过后就会刷新 last_visit_time ,这是他们也就会重新落到最近时间区间的 feeds 表中。
可以看出这是一种典型的以空间换时间的做法。关于区间划分应该依据目标系统的用户访问情况以及负载进行合理的设计,不同的情况可以划分出更多的分区,也可以适当减少分区以节省空间。

推与拉

此处输入图片的描述

上面介绍了很多关于推与拉的利弊,可以看出推的瓶颈最先体现在当用户粉丝增多时,其分发任务压力增大并且冗余数据也会越来越大。而拉的瓶颈在于随着系统的扩大,用户关注的人增多其查询效率都会受到影响并且给数据库越来越大的压力,后续缓存优化也会越来越复杂。那么很容易想到的一种策略就是结合推与拉模式来取各自的优点摒弃各自的缺点。

有一种想法便是区分用户,拿微博为例,微博中存在很多的僵尸用户僵尸粉,这些用户的活跃度极其的低,而在推模式下却不得不同时兼顾这些用户,那么有一种处理方案就是系统计算用户活跃度,然后那些粉丝数量超过一定数量的用户发布博文时只会把新的 feeds 主动推到活跃用户的timeline中(所谓活跃用户定义需要根据系统的具体情况进行具体定制,比如三天内活跃过的,随着系统地复杂度增加这个判定也可能变得极其复杂),同时将这条进入一个 feeds 池中。当不活跃的用户访问时,将从这个池中拉取并合入自己的 timeline 中。

这样的做法最重要的就是活跃用户的判定,判定算法的设计会直接影响到系统效率和用户体验。这只是一个想法,具体的可能会有各种各样的实现以及优化,只有我也会跟进更多相关的设计并对本主题进行更新。

添加新批注
在作者公开此批注前,只有你和作者可见。
回复批注