HyperLogLog比SET更省内存是因为它不存储原始值,仅用16384个寄存器记录最高位1的位置,通过调和平均估算基数,1000万ID仅需

HyperLogLog 为什么比 SET 更省内存
大促时用 SET 存用户 ID 做去重计数,内存涨得飞快——1000 万个 string 类型的 ID,实际占用可能超 200MB;而 PFADD 同样存 1000 万,内存通常不到 12KB。这不是玄学:HyperLogLog 是概率数据结构,不存原始值,只维护 16384 个寄存器(register),每个寄存器仅记录最高位 1 出现的位置,靠调和平均估算基数。
代价是:它不支持取具体元素、不保证 100% 精确(标准误差约 0.81%),但对“UV”“独立访客数”这类场景,完全可接受。
PFADD / PFCOUNT / PFMERGE 怎么配合用才不翻车
三个命令不是随便组合的。常见错误是把不同维度的 UV 拼成一个 key,比如把「商品页 UV」和「搜索页 UV」都往 uv:all 里 PFADD,结果 PFCOUNT 一算就偏高——HyperLogLog 不区分来源,只管是不是新值。
- 按业务维度拆 key:
uv:page:product:1001、uv:page:search:iphone - 聚合用
PFMERGE,别自己PFADD多次:合并多个 key 到新 key 再PFCOUNT,比逐个PFCOUNT后相加更准(避免交集重复估算) -
PFADD返回值是 1 表示该值首次插入,0 表示已存在——别拿它当「是否新增成功」判断依据,它不反映网络或写入状态
大促前必须检查的三个配置和行为
Redis 默认配置在高压下容易出问题,不是加机器就能扛住:
- 确认
maxmemory-policy不是noeviction:HyperLogLog key 被驱逐后,PFCOUNT返回 0,但业务可能还在持续PFADD,导致数据断层 - 禁用
lazyfree-lazy-user-del yes:大促期间PFMERGE产生的临时 key 删除不能延迟,否则会堆积大量待删对象,拖慢主线程 - 避免在 Lua 脚本里高频调用
PFADD:单次脚本中多次PFADD不会批量优化,反而增加 EVAL 开销;改用客户端分批或 pipeline
什么时候不该用 HyperLogLog
不是所有“去重计数”都适合它。如果业务要求:
- 需要列出具体哪些用户访问过 → 必须换
SET或布隆过滤器 + 回源 - 数据量长期低于 1000 →
SET的内存开销反而更低,且支持SCARD精确返回 - 要支持小时级/分钟级滑动窗口 UV → HyperLogLog 无法删除旧元素,得用时间分片 key + 定时清理,复杂度陡增,不如直接上
TimeSeries或 ClickHouse
真正省内存的关键,不是“用了 HyperLogLog”,而是“用对了边界”。大促压测时,盯着 used_memory_dataset 和 instantaneous_ops_per_sec 看,比背参数重要得多。










