最近公司需要做一个类似于知乎的产品,关于feed流,我了解了一下,简单的做了一个设计
feeds表结构
Id:
item_type: 关联物品的类型,例如:话题,回答
item_id: 关联物品的ID
user_id: 关联用户的ID
action_type: 行为,例如:创建,点赞,关注
案例:
如果有用户(id为1)进行话题的创建,生成对应话题(id 为100)的feed对象
id: 1
item_type: ‘Topic’
item_id: 100
user_id: 1
action_type: ‘create’
一个用户的的feed流查询流程:
- 先获取关注的用户列表
user_ids = Relationship.where(follower_id: current_user.id).ids
- 然后用户通过user_ids 来获取列表数据
feeds = Feeds.includes(:item).where(user_id: user_ids).order(created_at: :desc).page(page).per(per)
ps: 这里的关注是否除了关注用户,还需要关注话题等别的类型,如果需要或者将来有可能,那这里Feed表的user_id 属性,可能需要扩展为
follow_type 和 follow_id。
达到数据规模后的优化思路:
随着时间的推移数据量会达到一定的规模,从而导致上述获取数据的两条查询sql变慢,这时候可以给每一个用户维护一个用户feeds队列。
Redis的例子:
ID为1的用户,在redis中有一个key为user_1_feeds 的有序数组[1,23,58,56],数组中为feed的ID
用户获取他对应的Feed流就只需
ids = Redis.get(current_user.redis_key).page(page).per(per)
Feeds.includes(:item).where(id: ids)
当被关注用户有更新,有两种方式可以进行选择
推模式:
如果是像微博这种,用户有大量的粉丝情况,则要将他所新建的feed id推送给他的所有粉丝,也就是将他的粉丝每个人的redis队列中push这个最新的Id即可。
拉模式:
如果被关注用户不是这种大量粉丝的类型,则可以先获取用户队列中最前的一个feed的created_at,来查询大于这个created_at时间的feed,再将这些新查询的feedids 插入进他的redis队列中。
像微博,知乎这种规模feeds流进行操作就是两种的结合,会更复杂,大概是这样,它会维护一个在线用户列表,当某一个大V进行动态发布的时候,它只会给在线的用户进行push(feed_id)这种操作,防止僵尸粉丝或者不常在线的粉丝占用空间和计算。当这些不常上线的粉丝进行登陆的时候,再通过拉模式进行feed填充。
当用户有新关注,或者取消关注的操作时
看是否需要对之前的feed做修改,需要则就要像之前一样,重新计算feeds的数据,重新存入用户的redis队列中。
参考文章:
https://mednoter.com/design-of-feed-part-two.html
https://developer.aliyun.com/article/706808