UV统计
1.1 HyperLogLog
1.1.1 背景
UV :全称 Unique Visitor, 也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。 1 天内同一个用户多次访问该网站,只记录 1 次。
PV :全称 Page View, 也叫页面访问量或点击量,用户每访问网站的一个页面,记录 1 次 PV,用户多次打开页面,则记录多次 PV 。往往用来衡量网站的流量。
UV 统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到 Redis 中,数据量非常恐怖。
1.1.2 HyperLogLog 介绍
- Hyperloglog(HLL) 是从 Loglog 算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考: https://juejin.cn/post/6844903785744056333#heading-0
- Redis 中的 HLL 是基于 string 结构实现的,单个 HLL 的内存永远小于 16kb ,内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于 0.81 %的误差。不过对于 UV 统计来说,这完全可以忽略。
1.1.3 使用
| 命令 | 描述 |
|---|---|
PFADD key element | 将指定的元素添加到 HyperLogLog 中。 |
PFCOUNT key | 返回 HyperLogLog 的近似基数。 |
PFMERGE destkey sourcekey1 sourcekey2 | 将多个 HyperLogLog 合并为一个。 |
1.1.4 使用场景
- **适用 **:
- 统计网站每日独立访问量(UV)。
- 统计搜索关键词的搜索次数。
- 统计直播间在线人数峰值。
- **不适用 **:
- 需要获取具体的元素内容(HLL 不存储原始数据,只记录特征)。
- 数据量非常小(如果只有几十个用户,直接用
Set更精确且内存差异不大)。 - 容不得 1% 误差的场景(比如涉及资金结算)。
1.2 测试百万数据的统计
1.2.1 实现UV统计

1.2.2 代码实现
java
/**
*
* 测试HyperLogLog内存占用
*/
@Test
void testHyperLogLog() {
// 1. 准备数组,装用户数据
final int batchSize = 1000;
List<String> users = new ArrayList<>(batchSize);
// 2. 添加数据到HyperLogLog
for (int i = 1; i <= 1000000; i++) {
users.add("user" + i);
// 按已装载的数据量分批写入,避免i=0时误触发
if (users.size() == batchSize) {
stringRedisTemplate.opsForHyperLogLog().add("hll1", users.toArray(new String[0]));
users.clear();
}
}
// 循环结束后补写最后一批不足batchSize的数据
if (!users.isEmpty()) {
stringRedisTemplate.opsForHyperLogLog().add("hll1", users.toArray(new String[0]));
}
// 3. 统计数量
Long size = stringRedisTemplate.opsForHyperLogLog().size("hll1");
System.out.println("size = " + size);
}