场景分析

在社区项目中,我们有很多计数相关的需求,比如

  • 文章相关的:
    • 点赞数
    • 阅读数
    • 收藏数
    • 评论数
  • 用户相关的:
    • 用户发表的文章数
    • 用户文章的阅读总数
    • 用户文章被收藏数
    • 用户文章被点赞数
    • 用户粉丝数
    • 用户关注数

首先看一下我们的场景:

文章阅读数是个单独的表,每次从数据库查询文章详情的时候,DB中阅读量就会+1,然后返回。

除此之外,其他计数并不会直接存储在DB中,比如对于点赞数,我们在DB中存储的是什么呢?是点赞这个记录,而点赞数通过查询DB计算出来的,我们通过在SQL中使用sum函数计算出来。

我们再来看,在业务中,什么场景是高频的,什么场景是低频的。

  • 查询操作是高频的:对上述计数的查询是高频的,每次刷新页面,打开任何一个页面都需要查询。
  • 更新操作是低频的:很多人只是浏览查看,参与社区活动(点赞,评论,收藏)还是比较少的。

那么针对这种场景,我们的做法是这样的,

  1. 对这些计数的查询是比较高频的业务,如果每次都要查询数据库(使用SQL语句计算出来),是比较耗时的,因为可以基于Redis的incr指令实现一个计数器。
  2. 更新计数的话,我们的方案会把记录写DB。因为更新计数并不是一个高频的操作.

简而言之,在我们的项目中,每次点赞收藏等操作记录还是会写入数据库的,不过查询计数只需要和Redis打交道了。

我看网上一些文章是将整个点赞操作全存入Redis,再定期写回MySQL,感觉没太有必要,因为在我们的业务场景中点赞操作并不高频。https://juejin.cn/post/6844903703690870798

用户计数实现

我们将用户的相关计数,每个用户对应一个hash数据结构

  • key:user_statistic_${userId}
  • field
    • followCount:关注数
    • fansCount:粉丝数
    • articleCount:已发布文章数
    • praiseCount:文章点赞数
    • readCount:文章被阅读数
    • collectionCount:文章被收藏数

在业务中:

  • 更新时:更新DB,更新Redis
  • 查询时:只查询Redis就好了,不需要查询DB

文章计数实现

  • key:article_statistic_{articleId}
  • field:
    • praiseCount:点赞数
    • readCount:阅读数
    • collectionCount:收藏数
    • commentCount:评论数

在业务中:

  • 更新时:更新DB,更新Redis
  • 查询时:只查询Redis就好了,不需要查询DB

计数查询

直接hgetall就好了,甚是简单。

缓存一致性

我们采用简单的定时同步方案来实现,即:

  • 用户统计信息每天全量同步
  • 文章统计信息每天全量同步

同步频率每天一次就好啦,半夜进行。