Redis HyperLogLog 合并多个日活集合的计数方法
来源:17golang原创
时间:2026-11-13 13:25:44 302浏览 收藏
Redis HyperLogLog 合并多个日活集合时,可以直接使用多键 PFCOUNT 读取这些日期的并集估计人数;需要保存合并后的摘要,再使用 PFMERGE。不能把每天的 PFCOUNT 相加当作多日去重人数,否则跨天活跃的同一用户会重复计算。
这里的“集合”指用户集合的 HyperLogLog 摘要,不是 Redis Set。下面用三天活跃记录构建一个小型窗口统计方案。HyperLogLog 只保存近似计数所需的信息,不保存可枚举的用户清单。
Redis 官方文档:https://redis.io/docs/latest/develop/data-types/probabilistic/hyperloglogs/
明确统计窗口与用户标识
先约定统计的是同一应用 app42 中登录用户的活跃人数,按同一业务时区划分自然日。用户标识统一使用类似 uid:501 的稳定字符串;不能某天写整数文本、另一天写带前缀的字符串,也不能把设备 ID 与用户 ID 混用。
本文使用 uv:{app42}:day:20261109 等键。日期后缀确定数据所属日期;{app42} 是 Redis Cluster 的 hash tag,令同应用相关键落在同一槽中,方便执行多键统计。不同应用应使用不同标签;单个高流量应用也要评估同槽带来的负载集中。

写入三个独立的日活摘要
准备好可连接的 Redis 和 redis-cli 后,在测试命名空间运行下面的 Bash 示例。示例标识是虚构数据。若这些示例键已经被其他数据使用,请换一个全新的应用标签,避免把已有数据混入本次演示。
# 同一天重复上报同一标识,不表示新增一个不同用户。
redis-cli PFADD 'uv:{app42}:day:20261109' uid:501 uid:502 uid:503 uid:501
# uid:502 和 uid:503 跨天出现,合并时按同一个用户处理。
redis-cli PFADD 'uv:{app42}:day:20261110' uid:502 uid:503 uid:504
redis-cli PFADD 'uv:{app42}:day:20261111' uid:503 uid:505
PFADD 的整数回执表示内部摘要是否发生变化,不能当成精确新增人数。写入应用需要处理连接失败、超时和命令错误,不能无条件把事件标记为已统计。重复投递相同标识通常不会增加基数,但把错误用户 ID 写进去后,无法用删除单个成员的方式修正 HLL。
直接读取多日去重人数
# 仅查询这三个日期的并集估计,不建立长期汇总键。
redis-cli PFCOUNT 'uv:{app42}:day:20261109' 'uv:{app42}:day:20261110' 'uv:{app42}:day:20261111'
# 单独查询某一天时,只传入该日键。
redis-cli PFCOUNT 'uv:{app42}:day:20261109'
根据示例输入,数学上的三日并集包含 uid:501 到 uid:505 五个不同标识,各日集合大小相加则为八。这里给的是输入集合的逻辑推导,不是运行输出,也不保证任何规模下 HyperLogLog 都返回精确值。
Redis 文档给出的标准误差率约为 0.81%,它描述统计误差特性,不能理解成每次结果都被严格限制在正负 0.81% 以内。一次性窗口查询优先使用多键 PFCOUNT;日期数量很大、查询频繁时,它需要合并多个摘要,成本高于读取单个键,应测量业务延迟后决定是否缓存窗口摘要。
保存可复用的窗口摘要
如果仪表盘反复读取固定三日窗口,可以保存一个合并摘要。务必注意:PFMERGE 的目标键如果已经存在,它自身也会参与合并。因此不能把一个旧窗口汇总键反复当作任意新窗口的覆盖目标;已经进入目标的旧用户不会因为本次少传一个源日期就被减掉。
# 使用本次构建专用的新目标;实际任务应分配唯一构建标识。
redis-cli PFMERGE 'uv:{app42}:build:window-1109-1111-jobA' 'uv:{app42}:day:20261109' 'uv:{app42}:day:20261110' 'uv:{app42}:day:20261111'
# 从合并后的单个摘要查询窗口人数。
redis-cli PFCOUNT 'uv:{app42}:build:window-1109-1111-jobA'
# 示例保留一天,真实期限由报表读取与重算需求决定。
redis-cli EXPIRE 'uv:{app42}:build:window-1109-1111-jobA' 86400
示例要求 jobA 目标起初不存在。生产任务可用唯一构建 ID 创建新目标,确认构建命令成功后再在应用元数据中切换当前报表引用;失败时保留旧引用,清理失败构建。不要让两个并发任务共享同一个构建目标。PFMERGE 不会修改源摘要,但目标是合并时刻的数据,不会自动订阅之后到达的日活事件。

接入业务并安排保留周期
接入链路只需围绕三个职责组织:采集端规范用户标识和事件日期,统计端写入对应日键,查询端列出明确窗口的源键。晚到事件应写回事件实际所属日期;它会影响之后的多键查询,但已有窗口摘要要重新构建才能反映更新。
日键的保留期必须覆盖最长查询窗口、允许迟到的时间和必要重算时间。不要每收到一个事件就无条件把 TTL 重置为“从现在再留若干天”,否则频繁上报会不断推迟过期。可以由维护任务按日历边界设置到期时间;源键与窗口目标分别安排期限,不依赖合并自动继承业务保留规则。
| 业务需求 | 实现选择 | 要注意什么 |
|---|---|---|
| 临时查询跨日去重人数 | 多键 PFCOUNT | 源日期完整且属于相同统计口径 |
| 固定窗口重复读取 | 新目标 PFMERGE 后单键 PFCOUNT | 目标是快照,晚到数据需重建 |
| 逐日向前滑动窗口 | 重新选择日键并重建摘要 | 不能从旧 HLL 中减去过期一天 |
| 精确人数和用户明细 | 另用精确集合或明细存储 | 内存与计算成本需要另行设计 |
判断结果含义与常见问题
集成时应分别判断命令成功、日期输入完整和统计口径一致。一个正常返回的数字不证明源数据齐全。对于本应存在却因采集故障或提前过期而缺失的日键,应由数据完整性记录提示报表“不完整”,不能只把它当成当天无人活跃。
PFCOUNT 多个键会累加重复用户吗?
它估计多个摘要所代表集合的并集基数。同一用户跨天使用相同标识时,不应按出现天数重复计入;结果仍然是近似值。
能把 PFMERGE 的结果当成 Redis Set 枚举吗?
不能。它仍是 HyperLogLog 摘要,不支持列出所有用户,也不能直接查询某个用户是否属于窗口。
为什么重算更短的窗口人数没有下降?
先确认是否复用了旧目标键。旧目标本身参与合并,历史内容可能仍然保留;改用全新构建目标,并检查传入的日期列表。近似计数也不适合用微小差值判断业务变化。
多日统计能替代留存率计算吗?
不能直接替代。跨日并集人数和特定用户群的交集、留存是不同问题。不要用多个近似计数简单相减,就声称得到可靠的精确交集人数。
命令参考:https://redis.io/docs/latest/commands/pfcount/;https://redis.io/docs/latest/commands/pfmerge/。
-
411 收藏
-
246 收藏
-
498 收藏
-
267 收藏
-
100 收藏
-
303 收藏
-
数据库 · Redis | 1天前 | Redis · 慢查询 · 日志管理 · 性能排查 · redis slowlog 慢日志 SLOWLOG GET SLOWLOG LEN slowlog-max-len219 收藏
-
415 收藏
-
290 收藏
-
397 收藏
-
422 收藏
-
304 收藏
-
- 前端进阶之JavaScript设计模式
- 设计模式是开发人员在软件开发过程中面临一般问题时的解决方案,代表了最佳的实践。本课程的主打内容包括JS常见设计模式以及具体应用场景,打造一站式知识长龙服务,适合有JS基础的同学学习。
- 立即学习 543次学习
-
- GO语言核心编程课程
- 本课程采用真实案例,全面具体可落地,从理论到实践,一步一步将GO核心编程技术、编程思想、底层实现融会贯通,使学习者贴近时代脉搏,做IT互联网时代的弄潮儿。
- 立即学习 516次学习
-
- 简单聊聊mysql8与网络通信
- 如有问题加微信:Le-studyg;在课程中,我们将首先介绍MySQL8的新特性,包括性能优化、安全增强、新数据类型等,帮助学生快速熟悉MySQL8的最新功能。接着,我们将深入解析MySQL的网络通信机制,包括协议、连接管理、数据传输等,让
- 立即学习 500次学习
-
- JavaScript正则表达式基础与实战
- 在任何一门编程语言中,正则表达式,都是一项重要的知识,它提供了高效的字符串匹配与捕获机制,可以极大的简化程序设计。
- 立即学习 487次学习
-
- 从零制作响应式网站—Grid布局
- 本系列教程将展示从零制作一个假想的网络科技公司官网,分为导航,轮播,关于我们,成功案例,服务流程,团队介绍,数据部分,公司动态,底部信息等内容区块。网站整体采用CSSGrid布局,支持响应式,有流畅过渡和展现动画。
- 立即学习 485次学习