-
关注推送
-
关注推送也叫做Feed流,直译为投喂。为用户持续的提供“沉浸式”的体验,通过无限下拉刷新获取新的信息。
-
-
Feed流的模式
- Feed流产品有两种常见模式:
- Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈。
- 优点:信息全面,不会有缺失。并且实现也相对简单。
- 缺点:信息噪音较多,用户不一定感兴趣,内容获取效率低。
- 智能排序:利用智能算法屏蔽掉违规的、用户不感兴趣的内容。推送用户感兴趣信息来吸引用户。
- 优点:投喂用户感兴趣信息,用户粘度很高,容易沉迷。
- 缺点:如果算法不精准,可能起到反作用。
- Timeline:不做内容筛选,简单的按照内容发布时间排序,常用于好友或关注。例如朋友圈。
- Feed流产品有两种常见模式:
-
本例中的个人页面,是基于关注的好友来做Feed流,因此采用Timeline的模式。该模式的实现方案有三种:
- 1、拉模式
- 2、推模式
- 3、推拉结合
-
Feed流的实现方案1
- 拉模式:也叫做读扩散。
-
- 优点:比较节省内存空间。一般收件人的收件箱是空的,消息只在发件人的发件箱里保存一份,只有在收件人读取时才拉取发件人发件箱的信息(拷贝一份到收件人的收件箱,并按照时间做排序),读完之后不用了就会清理掉。
- 缺点:读取延迟较高。收件人每次来读消息时,都得重新拉取发件箱的消息,然后再做排序,这些操作一般耗时较久,所以延迟较高。
-
- 拉模式:也叫做读扩散。
-
Feed流的实现方案2
- 推模式:也叫做写扩散。
-
- 优点:读取延迟较低。发件人自身没有发件箱,当发件人有新消息时,直接将信息推送至收件人的收件箱进行存储并做好排序,收件人在读取信息时,不需要再做拉取信息的动作,直接读取收件箱即可。
- 缺点:内存空间占用较多。由于发件人没有发件箱,所以当发件人有新消息时,需要将消息在每个收件人的收件箱中都保存一份,因此内存占用较高。
-
- 推模式:也叫做写扩散。
-
Feed流的实现方案3
- 推拉结合模式:也叫做读写混合,兼具推和拉两种模式的优点。
-
-
模式说明:将发件人分为大V(粉丝较多)和普通人(粉丝较少),将收件人分为活跃粉丝(读取消息频繁)和普通粉丝(读取消息频率不高)。对于大V,由于粉丝众多,若直接给所有粉丝的收件箱推送消息,内存消耗过大,且由于大多粉丝读取消息的频率不高,只有少部分活跃粉丝,所以可以采取“高活跃度粉丝直接推送,低活跃度粉丝在读取时主动拉取”的策略,在发送消息时将消息在发件箱里保存一份,同时推送给活跃粉丝;对于普通人,由于粉丝较少,直接将消息推送至所有粉丝的收件箱即可。
-
- 推拉结合模式:也叫做读写混合,兼具推和拉两种模式的优点。
-
Feed流的三种实现方案对比
-
-
基于推模式实现关注推送功能
-
需求
- 1、修改新增探店笔记的业务,在保存blog到数据库的同时,推送到粉丝的收件箱。
- 2、收件箱满足可以根据时间戳排序,必须用Redis的数据结构实现。
- 思考:在Redis中,List和Set都可以实现排序和分页,那么当前场景应该选择谁呢?(先自己思考,后面会有分析)
- 3、查询收件箱数据时,可以实现分页查询。
-
Feed流的分页问题
- Feed流中的数据会不断更新,所以数据的角标也在变化,因此不能采用传统的分页模式。
-
- Feed流中的数据会不断更新,所以数据的角标也在变化,因此不能采用传统的分页模式。
-
Feed流的滚动分页
-
-
总结:根据以上分析,因为不断会有新的数据插入,角标是不断变化的,传统的分页模式会导致数据重复查询问题。但由于SortedSet可以通过ZRANGEBYSCORE命令来获取指定score范围内的元素,所以SortedSet胜出,最终选择使用SortedSet。
-
实现关注推送页面的分页查询(滚动分页查询)
-
需求:在个人主页的“关注”卡片中,查询并展示推送的Blog信息:
-
-
滚动分页查询功能分析
-
在Redis命令行窗口可以通过以下命令实现滚动分页查询:
- ZREVRANGEBYSCORE key Max Min WITHSCORES LIMIT offset count
-
滚动分页查询参数:
- max(最大分数):当前时间戳(第一次查询) 或 上一次查询的最小时间戳(除了第一次以外的查询)。
- min(最小分数):0(固定不变)。
- offset(偏移量):0(第一次查询) 或 在上一次的结果中,与最小值一样的元素的个数(除了第一次以外的查询)。
- count(分页数据个数):3 。
-
-
关键代码片段:
-
// 查询收件箱 ZREVRANGEBYSCORE key Max Min WITHSCORES LIMIT offset count
String key = FEED_KEY + userId;
Set<ZSetOperations.TypedTuple<String>> typedTuples = stringRedisTemplate.opsForZSet()
.reverseRangeByScoreWithScores(key, 0, max, offset, 2);// 解析数据: blogId、minTime(时间戳)、offset
List<Long> ids = new ArrayList<>(typedTuples.size());
long minTime = 0; // 2
int os = 1; // 2
for (ZSetOperations.TypedTuple<String> tuple : typedTuples) { // 5 4 4 2 2
// 4.1.获取id
ids.add(Long.valueOf(tuple.getValue()));
// 4.2.获取分数(时间戳)
long time = tuple.getScore().longValue();
if(time == minTime){
os++;
}else{
minTime = time;
os = 1;
}
}// 根据id查询blog
String idStr = StrUtil.join(conjunction: ",", ids);
List<Blog> blogs = query().in(column: "id", ids).last(lastSql: "ORDER BY FIELD(id," + idStr + ")").list();
// 这部分容易遗漏
for (Blog blog : blogs) {
// 5.1.查询blog有关的用户
queryBlogUser(blog);
// 5.2.查询blog是否被点赞
isBlogLiked(blog);
}
-
// 查询收件箱 ZREVRANGEBYSCORE key Max Min WITHSCORES LIMIT offset count
-
-
-
【Redis实战篇】Feed流(关注推送)与滚动分页查询
未经允许不得转载:171主机测评 » 【Redis实战篇】Feed流(关注推送)与滚动分页查询



