SpringBoot+Redis简单实现文章浏览量记录
时间: 2025-05-14 20:57:56 浏览: 26
### SpringBoot 和 Redis 实现文章浏览量统计
为了实现文章浏览量的记录功能,可以借助 Redis 的 HyperLogLog 数据结构来去重并计算独立访客的数量(UV),同时也可以简单地使用字符串类型的键值对来累加总访问次数(PV)。以下是具体的实现方式:
#### 1. 使用 HyperLogLog 记录 UV
Redis 提供了 `PFADD` 命令用于向 HyperLogLog 中添加元素,而 `PFCOUNT` 则用来获取当前集合中的近似唯一值数量。这种方式非常适合处理高并发场景下的 UV 统计。
```java
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;
@Service
public class ArticleService {
@Autowired
private StringRedisTemplate redisTemplate;
public void recordArticleView(String articleId, String userIp) {
// 定义 key,例如:"article:uv:{articleId}"
String uvKey = "article:uv:" + articleId;
// 添加 IP 地址到 HyperLogLog 结构中
redisTemplate.opsForHyperLogLog().add(uvKey, userIp);
// 更新 PV (Page View),定义 key 如:"article:pv:{articleId}"
String pvKey = "article:pv:" + articleId;
redisTemplate.opsForValue().increment(pvKey); // 自增操作
}
public long getUniqueViews(String articleId) {
// 获取 UV 数量
String uvKey = "article:uv:" + articleId;
return redisTemplate.opsForHyperLogLog().size(uvKey);
}
public long getTotalViews(String articleId) {
// 获取 PV 总数
String pvKey = "article:pv:" + articleId;
Object totalViewsObj = redisTemplate.opsForValue().get(pvKey);
if (totalViewsObj != null && !"".equals(totalViewsObj.toString())) {
return Long.parseLong(totalViewsObj.toString());
}
return 0L; // 如果不存在返回默认值 0
}
}
```
上述代码实现了两个主要的功能:
- **recordArticleView**: 每次有新的页面访问时调用此方法,传入文章 ID (`articleId`) 和用户 IP (`userIp`) 来记录一次访问行为。
- **getUniqueViews**: 返回指定文章的独立访客数量(UV)[^1]。
- **getTotalViews**: 返回指定文章的总访问次数(PV)。
#### 2. 处理定时任务同步数据至数据库
为了避免频繁读写数据库带来的性能瓶颈,可以在夜间或其他低峰期通过定时任务将 Redis 中的数据持久化到关系型数据库中。
```java
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
@Component
public class DataSyncTask {
@Scheduled(cron = "0 0 2 * * ?") // 每天凌晨两点执行
public void syncDataToDatabase() {
// 查询所有文章的 PV 和 UV 并保存到数据库
List<String> allArticles = getAllArticleIdsFromRedis(); // 获取所有文章ID列表
for (String articleId : allArticles) {
long pv = articleService.getTotalViews(articleId);
long uv = articleService.getUniqueViews(articleId);
saveToDatabase(articleId, pv, uv); // 调用 DAO 方法保存到 DB
}
}
private List<String> getAllArticleIdsFromRedis() {
Set<String> keys = redisTemplate.keys("article:*");
return new ArrayList<>(keys.stream()
.map(k -> k.replace("article:", ""))
.collect(Collectors.toSet()));
}
private void saveToDatabase(String articleId, long pv, long uv) {
// TODO: 实现具体逻辑,将数据插入或更新到数据库表中
}
}
```
这段代码展示了如何设置一个定时器,在每天固定时间触发并将 Redis 中的文章浏览统计数据批量导入数据库。
#### 3. 缓存优化与异常保护
考虑到实际生产环境可能会遇到诸如缓存穿透、缓存雪崩等问题,因此还需要采取一些额外措施保障系统的稳定性。比如可以通过为每个缓存 Key 设置带有随机偏移量的有效期限来减少集中过期的风险;另外还可以引入熔断机制防止突发流量冲击下游服务[^2]。
---
###
阅读全文
相关推荐



















