
1.UV统计UVUnique Visitor独立访客统计是互联网产品中最核心的数据指标之一。简单来说UV 统计的就是“有多少个不同的人访问了你的网站或功能”。为了让你更好地理解我们通常会把它和PV放在一起对比PV (Page View页面浏览量)不管是谁只要页面被打开/刷新了一次PV 就 1。如果我今天手贱按了 100 次 F5 刷新你的主页那你的 PV 就增加了 100。UV (Unique Visitor独立访客)基于用户的唯一标识比如用户 ID、IP 地址或设备号来去重。哪怕我今天疯狂刷新了 100 次你的主页对不起我只算 1 个 UV。在后端开发中UV 统计为什么是个“难题”既然 UV 的核心是去重那后端程序就必须“记住”今天到底有哪些用户来过。 如果你的网站日活只有几百人你可以直接把来过的 UserID 塞进 Java 的HashSet或者 Redis 的Set里通过集合天然的不可重复性来得出 UV。但是如果你的产品做大了比如百万级日活 把 100 万个用户的长整型 ID 全存进 Redis 的Set里单单为了统计今天这一个页面的 UV可能就要耗费几十上百 MB 的内存一个月就是几个 G如果有 10 个页面要统计呢内存直接被撑爆了。2.HyperLogLog (HLL)为了解决这种海量数据的去重统计问题Redis 提供了一个极其神奇的数据结构叫做HyperLogLog (简称 HLL)它天生就是用来做 UV 统计的。它的核心特点是极致省内存无论你放 1 万个用户还是 1 个亿的用户 ID 进去它在 Redis 里最多只占用 12 KB 的内存存在极小误差它是基于一种概率算法估算出来的标准误差率在0.81%左右。对于 UV 这种宏观统计数据比如报表上显示 100.2 万人访问和 100.4 万人访问运营是根本不在乎这千分之八的误差的。Redis HyperLogLog 的 API 设计非常极简它的核心命令只有 3 个所有命令都以PF开头是为了纪念这种数据结构算法的发明者 Philippe Flajolet。1. Redis 命令PFADD key element [element ...](添加元素)将一个或多个用户 ID 存入对应的 key 中。执行逻辑如果这是该用户今天第一次访问Redis 内部的基数估算发生了变化会返回1如果用户今天已经访问过已被记录则返回0。# 添加今天的 UV 记录 PFADD uv:2026-07-30 user_101 user_102PFCOUNT key [key ...](统计数量)获取一个或多个 key 的近似唯一总数误差率约为 0.81%。# 查看今天有多少独立访客 PFCOUNT uv:2026-07-30PFMERGE destkey sourcekey [sourcekey ...](合并统计)将多个 HyperLogLog 合并为一个新的 HyperLogLog。核心价值极度适用于统计周活跃用户 (WAU)或月活跃用户 (MAU)。因为你不能简单地把每天的 UV 数量相加同一个用户可能周一和周二都访问了直接相加算作 2 个人但用PFMERGE合并后底层算法会自动去重依然算作 1 个人。# 将周一到周日的 UV 数据合并到一个本周总UV的 key 中 PFMERGE uv:week_31 uv:2026-07-27 uv:2026-07-28 ...2.Spring Boot在 Java 后端工程中通常通过 Spring Data Redis 提供的StringRedisTemplate来操作 HyperLogLogpublic void hyperLogLogOperationsDemo() { String key1 uv:20260728; String key2 uv:20260729; String destKey uv:both_days; // 1. PFADD (添加元素) // 对应命令: PFADD key element [element ...] stringRedisTemplate.opsForHyperLogLog().add(key1, user1001, user1002); // 用户1001和1002在28日活跃 stringRedisTemplate.opsForHyperLogLog().add(key2, user1001, user1003); // 用户1001和1003在29日活跃 // 2. PFCOUNT (获取近似基数值) // 对应命令: PFCOUNT key [key ...] Long count1 stringRedisTemplate.opsForHyperLogLog().size(key1); Long count2 stringRedisTemplate.opsForHyperLogLog().size(key2); System.out.println( 28日UV数: count1); System.out.println( 29日UV数: count2); // 输出示例: // 28日UV数: 2 // 29日UV数: 2 // 3. PFMERGE (合并多个HyperLogLog) // 对应命令: PFMERGE destkey sourcekey [sourcekey ...] stringRedisTemplate.opsForHyperLogLog().union(destKey, key1, key2); // 再次使用 PFCOUNT 获取合并后的总人数 Long mergedCount stringRedisTemplate.opsForHyperLogLog().size(destKey); System.out.println(\n3. PFMERGE合并后的活跃总人数: mergedCount); // 输出示例: 3. PFMERGE合并后的活跃总人数: 3 (user1001在底层被算法自动去重) }