redis hyperloglog用12kb固定内存即可估算千万级uv,误差仅±0.81%,核心是pfadd批量添加(单次≤1024个稳定标识如user_id)、按天+业务维度建key并设ttl、pfcount多key合并为实时去重并集,不存原始数据,适用于运营看板等近似统计场景。

Java 中用 Redis HyperLogLog 做千万级 UV 统计,核心不是“存数据”,而是“做估算”——12KB 固定内存就能扛住上亿用户去重,误差仅 ±0.81%,关键在 PFADD 的调用逻辑、key 的设计方式和业务标识的选型。
PFADD 要批量加、看返回值、别当成功/失败用
PFADD 支持一次传多个用户 ID,但不是逐个判断,而是整体去重后更新内部寄存器:
- 单次最多传 1024 个 ID;超了必须拆条,否则截断(Jedis 默认不分片,得自己切)
- 返回 1 表示「至少有一个新用户被识别」,返回 0 表示「本次所有 ID 都已被该 key 记住」——这不是操作失败,是正常去重反馈
- 用户标识必须稳定:优先用 user_id 或 device_id,不用 session_id、临时 token 或 IP(易变、不准)
- 空字符串 ""、数字 123、JSON 字符串都合法,但业务上得能唯一对应一个自然人
Key 必须按天+业务维度建,且设 TTL 防内存堆积
每个 HyperLogLog 固占 12KB,不随数据量增长,但 key 不清理就会一直占内存:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
- 推荐格式:uv:page:home:20260721(页面维度)、uv:channel:ios:20260721(渠道维度)
- 禁止混用:App 和 PC 的 user_id 不能塞进同一个 key,否则跨端同人会被误判为一人
- PFADD 不自动设过期,首次写入后必须立刻执行 EXPIRE,比如存活 24 小时:jedis.expire("uv:page:home:20260721", 86400)
- 更稳妥做法:用定时任务清理 7 天前的 key,不依赖 Redis 自动过期(可能延迟或被禁用)
PFCOUNT 查总数,多 key 合并是实时并集,不是相加
查单日 UV 直接 PFCOUNT 单 key;查周 UV 用多 key 合并,结果是去重后的并集:
- 代码示例:jedis.pfcount("uv:page:home:20260715", "uv:page:home:20260716", ..., "uv:page:home:20260721")
- 这等价于内存中临时 PFMERGE 再估算,某用户连续 7 天访问也只算 1 次
- 纯读操作,不改源 key,但高并发下频繁多 key 查询会吃 CPU,报表类任务建议错峰执行
- 日常监控用单 key,汇总分析才用多 key,避免无谓开销
它不能回溯、不能精确,但特别适合“总量快、合并易”的场景
HyperLogLog 不存原始 ID,所以天生有边界:
- 无法查「今天 UV 里 iOS 用户有多少」——需要配合 Bitmap 或落库日志做二次筛选
- 无法知道漏了谁、多了谁——只保证整体误差 ≤0.81%,对运营看板、大促实时大盘完全够用
- 不能替代 Set 做登录校验、权限判断,也不适用于财务、审计等强一致场景
- 想验证估算是否合理?可用 DEBUG OBJECT key 看 encoding 类型,但生产环境慎用
Java免费学习笔记:立即使用
解锁 Java 大师之旅:从入门到精通的终极指南










