博客网站:利用redis统计文章点击量
前言
最近在做一个分布式博客系统,经过调研,发现常见博客网站普遍都有一个点击量的功能,就是当用户点击了某文章,会统计一个点击量,或者称为阅读量。并且这个点击量的统计应该经过了一定的过滤,例如最基本的就是根据ip过滤,每个ip的多次访问只算一个点击量等等。我在自己的项目中也想实现这样的效果,下面分享一下实现的过程。
技术选型
这里就简单的把ip作为筛选条件,即同一个ip的多次点击算作一个点击量。
mysql
最简单的方案就是当用户访问文章的预览页面时,把文章的id和用户的ip存到一张表中,后续用户再次访问这篇文章时,去表中查询是否已经存在记录,如果不存在 那就算一个点击量,如果存在,就不增加点击量。
这个方案是最简单的,但是也是问题最大的。因为文章的预览 是不需要登录的,用户的每一次点击都会往这个表插入数据,时间长了,这个表的数据量会非常大,导致在再次判断当前ip是否点击过时查询效率低下。并且这些数据感觉全部存到数据库也没有必要,因为这更像是一个日志数据,并不是非常重要的业务数据。
redis 单个key存储一个ip
第二个方案是利用了redis,具体做法为:在用户点击文章时,拼接一个key:BLOG_CLICK_文章id_用户ip
,然后查询是否存在这个key,如果存在,说明这个ip已经点击过当前文章,就不算点击量,反之说明用户还未点击过这篇文章。
这个方案比上面的方案要好,因为它将用户的点击信息保存到了redis中,redis的访问速度肯定是要比mysql快的,但是缺点也很明显,这个方案势必会产生非常多个key,因为每个文章和每个ip 都会生成一个key。这么多key肯定会对redis造成压力,会占用大量的空间,影响redis的性能。所以这个方案虽然比上面的好,但是也不推荐。
redis 使用set结构
我最后的实现方案就是使用了redis里面的set类型,在set中,不会存在重复数据,当我add进去一个ip,如果这个ip已存在,说明这个ip曾点击过文章;反之就是没有点击过,此时给他计算点击量。使用StringRedisTemplate操作set集合的add方法时,会返回一个Long值,为1时 代表插入了一个新数据,为0时 代表未插入新数据。我们就可以根据返回的这个Long值来判断当前ip是否已存在于set集合中。这样的话,key的数量比起上一个方案会少很多,也算是目前一个比较合适的方案吧
另外,我的业务里,给这个缓存设置了一个过期时间,是到当月的最后一天,也就是每个月统计一次点击量,也算是为了不让redis中长时间的存在某些不变的key,并且这些key的长度还可能会越变越大。
下面简单贴一下代码:
@Autowired
private StringRedisTemplate redisTemplate;
@GetMapping("/{id}")
public Result getBlogById(HttpServletRequest request, @PathVariable("id") String id) {
BlogPreviewVo vo = blogService.getBlogPreviewById(id);
if (vo != null) {
//统计点击量
String ip = IpUtil.getClientIp(request);
String key = "BLOG_CLICK_IP_SET_" + id;
Long l = redisTemplate.opsForSet().add(key, ip);
Date date = new Date();
DateTime dateTime = DateUtil.endOfMonth(date);
redisTemplate.expire(key, (dateTime.getTime() - date.getTime()) / 1000, TimeUnit.SECONDS);
if (l > 0L) {
//代表这是一个新ip 返回的vo中点击数+1
vo.setClickCount(vo.getClickCount() + 1);
//异步更新文章的点击数
blogService.updateClickCount(id, 1L);
}
return Result.success(vo);
}
return Result.redirect("文章不存在");
}
总结
以上,就完成了一个通过使用redis中的set结构,来统计文章点击量的案例。
转载自:https://juejin.cn/post/7388281982985502755