Java面试题:Redis统计独立用户访问量
题目描述
请使用Redis设计一个统计网站独立用户访问量(UV)的方案,要求:
- 支持每日/每月UV统计
- 内存占用高效(百万级用户)
- 说明Java代码如何调用Redis实现
通俗解答(含故事举例)
假设你开了一家"程序员咖啡店",每天需要统计有多少不同的顾客光顾(排除重复进店的人)。
🌰 故事场景
- 周一:顾客A、B、C来访
- 周二:顾客A、D来访
- 真实UV = 4人(A/B/C/D)
如果用普通计数器:
- 记录所有顾客ID → 内存爆炸 💥
- 查重效率低 → 影响咖啡机响应速度 ☕
Redis解决方案:HyperLogLog
Redis的HyperLogLog
(HLL)是专门解决这类问题的神器!
特点:
- 固定12KB内存,可统计亿级数据
- 误差仅0.81%
- 支持合并统计(如:月UV = 每日UV合并)
📊 Redis核心命令
# 添加用户访问
PFADD uv:20250806 "userA" "userB"
# 获取当日UV
PFCOUNT uv:20250806 # 返回2
# 合并多日数据(自动去重)
PFMERGE uv:month_08 uv:20250806 uv:20250807
Java代码实现
import redis.clients.jedis.Jedis;
public class UVCounter {
public static void main(String[] args) {
try (Jedis jedis = new Jedis("localhost", 6379)) {
// 1. 模拟用户访问
String todayKey = "uv:" + LocalDate.now(); // 例如:uv:2025-08-06
jedis.pfadd(todayKey, "user123", "user456", "user123"); // user123只计1次
// 2. 获取当日UV
Long dailyUV = jedis.pfcount(todayKey);
System.out.println("今日UV: " + dailyUV); // 输出2
// 3. 合并为月统计(每月1日执行)
String monthKey = "uv:month_202508";
jedis.pfmerge(monthKey, "uv:20250801", "uv:20250802", todayKey);
}
}
}
🔑 关键点
- 去重原理:HLL用概率算法估算基数(类似"抛硬币估算人数"🎲)
- 高性能:添加/查询时间复杂度都是 O(1)
- 适用场景:点击流统计、DAU/MAU报表
🌟 总结
方案 | 内存占用 | 精度 | 适用规模 |
---|---|---|---|
Set存ID | 高(O(n)) | 100% | 小规模 |
HLL | 极低(12KB) | 99.19% | 海量数据 |
✅ 优先选择HyperLogLog:在容忍微小误差时,它是内存和性能的最佳平衡。
思维导图
图:HyperLogLog工作原理示意图
以上内容由AI搜集并生成,仅供参考