4 从小白到架构师: Feed 流系统实战( 二 )


至于不活跃的用户,在他们回归后使用拉模型重新构建一下关注 Timeline 就好了 。因为不活跃用户回归是一个频率很低的事件,我们有充足的计算资源使用拉模型进行计算 。

4 从小白到架构师: Feed 流系统实战

文章插图
因为活跃用户和不活跃用户常常被叫做「在线用户」和「离线用户」,所以这种通过推拉结合处理头部作者发布内容的方式也被称为「在线推,离线拉」 。
再优化一下存储在前面的讨论中不管是「关注 Timeline」还是关注关系等数据我们都存储在了 MySQL 中 。拉模型可以用 SQL 描述为:
SELECT *FROM articlesWHERE author_uid IN ( SELECT following_uid FROM followings WHERE uid = ?)ORDER BY create_time DESCLIMIT 20;通过查看执行计划我们发现,临时表去重+Filesort、这个查询叠了不少 debuff:
4 从小白到架构师: Feed 流系统实战

文章插图
至于推模型 , 关注 Timeline 巨大的数据量和读写负载就不是 MySQL 能扛得住的 。。。
遇事不决上 Redis显然关注 Timeline 的数据是可以通过 articles 和 followings 两张表中数据重建的,其存在只是为了提高读取操作的效率 。这有没有让你想起另外一个东西?
没错!就是缓存,关注 Timeline 实质上就是一个缓存,也就是说关注 Timeline 与缓存一样只需要暂时存储热门数据 。
我们可以给关注 Timeline 存储设置过期时间,若用户一段时间没有打开 App 他的关注 Timeline 存储将被过期释放 , 在他回归之后通过拉模型重建即可 。
在使用「在线推,离线拉」策略时我们需要判断用户是否在线,在为 Timeline 设置了过期时间后,Timeline 缓存是否存在本身即可以作为用户是否在线的标志 。
就像很少有人翻看三天前的朋友圈一样,用户总是关心关注页中最新的内容 , 所以关注 Timeline 中也没有必要存储完整的数据只需要存储最近一段时间即可,旧数据等用户翻阅时再构建就行了 。
鲁迅有句话说得好 ——「遇事不决上 Redis」 , 既然是缓存那么就是 Redis 的用武之地了 。
4 从小白到架构师: Feed 流系统实战

文章插图
Redis 中有序数据结构有列表 List 和有序集合 SortedSet 两种 , 对于关注 Timeline 这种需要按时间排列且禁止重复的场景当然闭着眼睛选 SortedSet 。
将 article_id 作为有序集合的 member、发布时间戳作为 score, 关注 Timeline 以及个人 Timeline 都可以缓存起来 。
在推送新 Feed 的时候只需要对目标 Timeline 的 SortedSet 进行 ZAdd 操作 。若缓存中没有某个 Timeline 的数据就使用拉模型进行重建 。
在使用消息队列进行推送时经常出现由于网络延迟等原因导致重复推送的情况,所幸 article_id 是唯一的 , 即使出现了重复推送 Timeline 中也不会出现重复内容 。
这种重复操作不影响结果的特性有个高大上的名字 ——— 幂等性
当 Redis 中没有某个 Timeline 的缓存时我们无法判断是缓存失效了 , 还是这个用户的 Timeline 本来就是空的 。我们只能通过读取 MySQL 中的数据才能进行判断,这就是经典的缓存穿透问题 。
对于时间线这种集合式的还存在第二类缓存穿透问题,正如我们刚刚提到的 Redis 中通常只存储最近一段时间的 Timeline,当我们读完了 Redis 中的数据之后无法判断数据库中是否还有更旧的数据 。
这两类问题的解决方案是一样的,我们可以在 SortedSet 中放一个 NoMore 的标志,表示数据库中没有更多数据了 。对于 Timeline 本来为空的用户来说,他们的 SortedSet 中只有一个 NoMore 标志:
4 从小白到架构师: Feed 流系统实战

文章插图
最后一点:拉取操作要注意保持原子性不要将重建了一半的 Timeline 暴露出去:
4 从小白到架构师: Feed 流系统实战

文章插图
总结一下使用 Redis 做关注时间线的要点:
  • 使用 SortedSet 结构存储,Member 为 FeedID,Score 为时间戳
  • 给缓存设置自动过期时间,不活跃用户的缓存会自动被清除 。使用「在线推,离线拉」时只给 Timeline 缓存未失效的用户推送即可
  • 需要在缓存中放置标志来防止缓存击穿
一层缓存不够再来一层虽然 Redis 可以方便的实现高性能的关注 Timeline 系统,但是内存空间总是十分珍贵的 , 我们常常没有足够的内存为活跃用户缓存关注 Timeline 。

推荐阅读