订阅关注及好友动态的Feeds流设计

     订阅关注和好友动态更新的feeds是常见的业务功能, 我们知道像人人、微博、推特这样的应用,做feeds信息流相当专业 !   现在的social产品应用就算是没有好友动态,也会有订阅推送的.   根绝业务的量级我们会选定适合自己的方案, 像订阅好友动态业务量级不大的时候,怎么招都是可以.   但订阅及好友的数量关注度过大那就相当麻烦了…


    该文章写的有些乱,欢迎来喷 ! 另外文章后续不断更新中,请到原文地址查看更新.     http://xiaorui.cc/?p=4033


    业界关于订阅Feeds信息流的实现,通常会选用下面几个方案, push、pull、push + pull .


    推送模式, push mode

订阅表:
关注人
发送人
发表时间
balabala ... ..

     这个对于用户来说查询性能是最好的, 他只需要扫描关注人是他的timeline数据就可以了.  sql语句是  where  关注人 = xxx and publish_time > xxx limit 100 ,  索引也是最优的….   如果想使用这种高效的sql查询,必然会造成数据冗余的问题。 另外推送量级过大必然会造成一定的推送延迟 .   其实据我所知,大多数应用采用 Push的方法多一点 .


     这样我们拿微博第一红人姚晨来说,他的粉丝有将近几百万人。那如果使用推送的模式的化,姚晨发了一个信息,那么我们后端就要给100w的粉丝发送订阅提醒。 100w条记录呀, 还有我们知道有僵尸粉这个说法,虽然有不少人关注了姚晨,但是那些僵尸粉最多几个月登陆一次,甚至以后就不登陆了,这样的信息推送必然是浪费空间的….    对于这么大的数据是必须考虑分库分表,毕竟 一个名人一个信息 就100w条记录….  分库分表可以按照 用户ID来切分,或者 发表时间来切分….  我建议使用用户ID来切分,这样能降低数据的热点问题, 这里就不详细阐述分库分表了,有兴趣的朋友可以看看我写过的mysql文章. 


     100w的推送量肯定不能用单线程来处理,这时候我们可以扩展分布式消息队列,  不要把100w个订阅人推送到MQ里面,因为生成任务推送也是有时间消耗的,最好的方法是 扔给每个worker一个区间 , 每个worker拿到自己的区间并推送 .


    拉取模式, pull mode


一周订阅表:
关注人
发送人
发表时间
balabala ... ..


一个月订阅表:
关注人
发送人
发表时间
balabala ... ..

三月订阅表:
关注人
发送人
发表时间
balabala ... ..

所有订阅表:
关注人
发送人
发表时间
balabala ... ..

    看起来他跟 push推送模式差不多,只是多了不少订阅的时间区间表 .   他的优点在于减少了数据的冗余,在推送模式下你需要把信息发给所有的订阅人,他的时间复杂度是 O( N ) .  所谓的Pull模式就是当用户登陆的时候再去从这些表里面拿出订阅信息..


feeds表时间区间可划分为 最近7天,最近一个月,近三个月 及所有时间数据.   姚晨每次发信息我们确保会入库到 近一周数据表, 然后通过消息队列异步刷新到其他几个表.    为了保持数据的时效性,需要写个脚本来刷新删除过期的数据.   
需要明确的一点,订阅信息是可以适量丢失的,当然还是要尽量保证数据稳定性的….  
当用户登陆的时候,我们通过用户的上次的登录时间确定他适合查询那个区间表。 对于每天都登录的用户定位到近一周表 !半个月登录一次的,定位到一个月表。  以此类推呀!!!

不需要过度依次去查询,只跳跃一个表,一周没有的,就去近一个月订阅表瞅瞅! 一般来说是不需要再去三个月订阅表.     一般应用的订阅信息流是推广信息,个性推荐,广告参杂一起的,所以更加没必须要一定要把关注的信息拿到.

Pull模式最大的优点在于节省存储空间,缺点就是 性能不在地… 因为你的sql查询语句是  where  sender in ( uid1, uid2, uid3 … … ) and publish_time > xxxx ;     sql的in性能是让人无语的, 当sql操作符不确定时索引利用率是最低的。    这样的查询注定是花费时间的,解决方法是在业务层面异步加载数据,当你打开App的时候,我们可以预先就去拉取他的数据, 这样当用户打开订阅功能时, 就可以及时看到订阅信息了。


不管是推送和拉取各有利弊的….  那么有没有结合在一起的方法?  有的….    


推拉结合模式

一句话,在线推送,离线拉取 !

首先要标识 用户活跃, 当用户A发信息的时候,异步任务会判断关注A的人有谁在线…   然后push 信息流到关注用户的 user_feeds redis zset ,  score为时间戳, 当然发送的信息还是要扔到 订阅表的里… 如果用户不在线,那么走下面的Pull按需拉取模式…   当然订阅表还是要按照时间区间划分的.

在线用户如何标识?   用户打开app就活跃一次,那么就暂定为是在线状态, ttl 为 3天,  根据场景可以适当的延长… 

没有写完… … 


    


大家觉得文章对你有些作用! 如果想赏钱,可以用微信扫描下面的二维码,感谢!
另外再次标注博客原地址  xiaorui.cc

发表评论

邮箱地址不会被公开。 必填项已用*标注