订阅关注和好友动态更新的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模式就是当用户登陆的时候再去从这些表里面拿出订阅信息..
不需要过度依次去查询,只跳跃一个表,一周没有的,就去近一个月订阅表瞅瞅! 一般来说是不需要再去三个月订阅表. 一般应用的订阅信息流是推广信息,个性推荐,广告参杂一起的,所以更加没必须要一定要把关注的信息拿到.
Pull模式最大的优点在于节省存储空间,缺点就是 性能不在地… 因为你的sql查询语句是 where sender in ( uid1, uid2, uid3 … … ) and publish_time > xxxx ; sql的in性能是让人无语的, 当sql操作符不确定时索引利用率是最低的。 这样的查询注定是花费时间的,解决方法是在业务层面异步加载数据,当你打开App的时候,我们可以预先就去拉取他的数据, 这样当用户打开订阅功能时, 就可以及时看到订阅信息了。
不管是推送和拉取各有利弊的…. 那么有没有结合在一起的方法? 有的….
推拉结合模式
一句话,在线推送,离线拉取 !
在线用户如何标识? 用户打开app就活跃一次,那么就暂定为是在线状态, ttl 为 3天, 根据场景可以适当的延长…
没有写完… …