现如今在面向C端的社交场景里面,大家都会接触到feed流,我们最熟悉的feed流就是微信朋友圈和微博了。
今天我们就来从技术的角度谈谈关于这些feed流信息的存储与展示。这篇文章我们主要介绍的是推模式。
存储阶段
首先我们从存储来看这种推模式。关于这种存储,他主要体现在3个方面的存储:
1)第一张表是用户信息的存储
用户信息的存储主要是博主和粉丝这些用户的存储,所以这里肯定是有一张user表的,形如:
这张表是标准的用户信息表,在真实的业务中其实还有很多字段没有列举出来。里面主要存储的是整个用户体系。
备注:这里不考虑大型系统的分库分表场景
2)第二张表是用户的关注信息存储
用户的关注信息存储,主要设计的业务就是用户的关注和粉丝信息的表,形如:
这张表里面主要存储的是用户的关注关系,我们可以从这个表中查询到当前用户关注了哪些人,也可以通过这个表查询到当前用户有哪些粉丝。
3)第三张表用来存储原始feed流
用户发布的所有feed流都是永久存储的,因此需要把他进行持久化存储起来,形如:
重点来了,既然是推模式,那么就相当于某个人发了一个feed流,那么就需要给关注他的人的所有收件箱都投送一封信息。也就是第四张表。
4)第四张表用来作为feed流粉丝的收件箱
这张表相当于张三发了一条feed流,那么所有关注张三的都需要在这里作为收件箱,接收一条feed记录,形如:
那么上诉整个流程的话在存储阶段的整个流程就是:
1、张三发了一条feed流
INSERT INTO `test1`.`feeds` (`feed_id`, `pub_user_id`, `feed_context`, `status`, `cts`, `uts`) VALUES (1, 1, '这是第一条feed流', 1, '2023-02-01 14:06:44', '2023-02-01 14:06:44');
2、查询张三的所有粉丝
select * from user_followers where target_user_id = 1;
3、批量向张三的粉丝插入当前的feed流信息
INSERT INTO `test1`.`feeds_inbox` (`id`, `feed_id`, `target_user_id`, `cts`) VALUES (1, 1, 1, '2023-02-01 14:08:43'); INSERT INTO `test1`.`feeds_inbox` (`id`, `feed_id`, `target_user_id`, `cts`) VALUES (2, 1, 2, '2023-02-01 14:08:43'); INSERT INTO `test1`.`feeds_inbox` (`id`, `feed_id`, `target_user_id`, `cts`) VALUES (3, 1, 3, '2023-02-01 14:08:43');
查询阶段
这个推的方式由于在发feed流的时候已经把关系建立好了,所以这里查询就非常简单,直接查对应的表即可:
select feeds.* from feeds_inbox join feeds on feeds_inbox.feed_id = feeds.feed_id where target_user_id = 1 ORDER BY feeds.cts desc limit 10;
以上就是一个完整的关于feed流的推拉存储模式之推模式的设计思路。从上面我们可以看出哪些问题呢?
1)时效性很差
像微博里面,很多明星的粉丝都是上亿级别,这时候如果这个明星发了一条微博之后,我们要插入上亿条数据,那么在插入的时候这个可想而知是非常慢的。所以这里在插入的时候我们可以使用如下的解决方案:
1、将消息发送到消息队列中,利用多分片,多消费者的概念来消费数据,这样可以尽可能快的插入数据,进一步降低feed流推送的时效性。 2、使用更高性能的存储数据库,例如将mysql替换成doris或者clickhouse这样的存储方案。
2)存储成本很高
这里一个人都多少粉丝就会生成多少条记录,那么在C端场景这个存储由于用户的活跃度是非常吓人的,因此在存储的时候我们需要考虑很多,可以使用如下的方案:
1、使用冷热数据进行存储,也就是我们通过部分数据存储产品的特性,把超过xxx天的数据或者不经常活跃用户的数据等条件作为规则,满足规则就是冷数据,不满足规则就是热数据,这样子通过规则配置进行存储的转移。 2、使用现在的一些大数据数仓或者数据湖的存储产品,以很方便的进行扩容。
3)取消关注后的收件箱是否存储?
如果之前A用户关注了B用户,B用户发送的feed的时候我们已经把A用户的收件箱塞进去了很多条数据,这时候A用户突然取消关注B用户,此时是否需要把收件箱的内容给删除掉?这块可以使用如下的解决方案:
1、直接进行物理删除收件箱的对应数据(为什么不是逻辑删除呢,因为这里如果是逻辑删除的话,这些数据本来就不用,而且这个表已经很大了,增加了非常大的存储和查询压力,同时逻辑删除也会带来重新关注的时候跑脚本做saveOrUpdate.) 2、在查询的时候,使用not in (取消关注的用户id),也就是在查询的时候过滤掉。
4)首次关注或者取消关注后重新关注的收件箱数据存储
如果用户是首次关注,那么肯定是需要利用脚本把用户的所有feed流都向收件箱跑一边,用以统一查询数据,这块可以使用如下的解决方案:
1、把用户发过的所有feed流id都是用redis存储起来,只要关注,直接从redis查询feed id集合,直接插入收件箱即可。
当然能看到此方案上面问题,但是我们也要看到此方案的优点,那么有哪些优点呢?
1)这是一个满足读多写少的场景,可以充分利用缓存的优势来做数据的展示(额外说一下,据说新浪是全中国使用redis集群最大的业务公司)
2)做个性化推荐的时候,可以很方便的扩展根据feed的价值做自定义打分展示。
还没有评论,来说两句吧...