HyperLogLog 基数统计与误差

举报
yd_287908631 发表于 2026/11/07 07:31:37 2026/11/07
【摘要】 讲清 HyperLogLog 0.81% 误差来源与 UV 统计的适用边界

统计独立访客、独立 IP、曝光去重,精确方案(Set、Hash)的内存随基数线性增长,一个亿级 UV 的页面存一天就要数 GB。HyperLogLog(下称 HLL)用固定约 12 KB 的内存换来 0.81% 的标准误差,是"只要数量不要名单"场景的标准答案。本文以 Redis 7 为基准讲清它的用法、误差来源与适用边界。

一、为什么需要基数估计

UV 这类指标的特点是:只需要"有多少个不同的",不需要"具体是谁"。精确去重必须保存全部成员,内存下不去;而估计类算法允许极小误差,就能把内存压到常数级。HLL 的原理一句话可以讲完:对每个元素做哈希,按哈希值的前若干位把元素分到 16384 个桶,桶里记录"哈希值二进制末尾连续 0 的最大个数",最后对 16384 个桶的值做调和平均并修正偏差,得到基数估计。直觉是:出现"末尾很多个 0"的哈希值很罕见,观察到的最大连续 0 越长,说明见过的元素越多。

16384 个桶、每桶 6 bit,对应约 12 KB 的密集存储;误差由桶数决定,1.04 / sqrt(16384) 约 0.81%,这就是官方标准误差的出处。桶数是写死的 2^14,单 key 的误差精度因此固定,不存在"调参提高精度"的选项,想更准只能换方案。

二、三个命令

HLL 只有三个命令,行为却有细节:

127.0.0.1:6379> PFADD uv:20261025 "u:1001" "u:1002"
(integer) 1
127.0.0.1:6379> PFADD uv:20261025 "u:1001"
(integer) 0
127.0.0.1:6379> PFCOUNT uv:20261025
(integer) 2
127.0.0.1:6379> PFMERGE uv:week uv:20261019 uv:20261020
OK
127.0.0.1:6379> PFCOUNT uv:week
(integer) 2
  • PFADD:加入元素,返回 1 表示内部寄存器可能被更新(基数估计可能变化),返回 0 表示几乎没变;不传元素可以只建 key。PFADD 对同一元素重复添加是幂等的,重复请求不会改变估计值,客户端不需要预先去重。
  • PFCOUNT:返回基数估计。单 key 时结果会被缓存,但可能触发稀疏到密集的编码转换,是带写副作用的命令;传多个 key 时会现场合并计算,开销随 key 数与数据量增长,不要放进高频调用路径。
  • PFMERGE:把多个 HLL 合并到目标 key,目标内容被完整覆盖,不是追加。

按天建 key、PFMERGE 聚合成周月视图,是 UV 统计的标准结构,任何时间段的 UV 都可以即时合并出来。天 key 的命名带上统计口径,如 uv:page:home:20261025;页面多、口径多时 key 会成倍增长,每个都是 12 KB 起步,天 key 设 7 到 30 天 TTL,长周期视图由定时任务 PFMERGE 到月 key、季 key 并单独管理生命周期。

三、误差应该怎么看待

0.81% 是标准差,不是上限。绝对误差随基数增长:100 万 UV 时约 ±8100,1 亿 UV 时约 ±81 万。小基数时 HLL 走稀疏编码,误差远小于这个数字,但任何基数下它都是估计值,包括只有两三个元素的时候。由此推出使用边界:

  • 可以用:报表大盘、趋势分析、风控粗筛、容量预估——差 1% 无关痛痒的场景。
  • 不能用:对账、佣金结算、资格判定、限流阈值判断——差一个用户都不行的场景。

两个常被问到的点:第一,合并后的误差不叠加放大,N 个 HLL 合并的估计值仍是 0.81% 量级的标准差,这是 HLL 相比"先精确统计再相加"最大的工程价值;第二,HLL 之间没有重复计算问题,同一元素加进多个 HLL 再合并,结果与直接合并原始集合一致。

评估误差是否可接受有个简单办法:把业务的最小决策粒度和误差绝对值对齐。页面改版分析的 UV 是十万级,±800 的波动不影响结论;运营结算的 UV 是三千级,±25 就可能改变档位判定,这种量级就不要用 HLL。风控场景 HLL 还有个典型用法是基数突变检测:每分钟建一个 key,环比增速异常(UV 秒级翻倍)往往意味着爬虫或攻击,而检测只需要先后读两个 PFCOUNT。

四、适用边界与替代方案

HLL 的限制同样明确:只有 PFADD 没有删除,写脏了只能 DEL 重建;不能判断"某个元素是否统计过"——那是 Set 和布隆过滤器的职责。三种去重方案的对比:

| 方案 | 内存 | 精确性 | 支持删除 | 判存 |

| --- | --- | --- | --- | --- |

| Set | 随基数线性增长 | 精确 | 是 | 是 |

| Bitmap | 与 ID 域大小成正比 | 精确 | 是 | 是 |

| HLL | 固定约 12 KB | 约 0.81% 误差 | 否 | 否 |

选型口诀:要名单和判存用 Set;ID 域密集且要精确计数用 Bitmap;只要个数、容忍 1% 误差、基数巨大用 HLL。三者也可以组合:日粒度用 Bitmap 保精确,历史长周期用 HLL 控内存。组合的落地结构:当天 UV 用 Bitmap 记(可判存、可精确),次日凌晨由定时任务把基数写入普通 String 存档,同时 PFADD 进 HLL 供跨天聚合,Bitmap 满一周过期。这样报表里既有精确数,又有低成本的任意区间估计。容量规划上 HLL 也最省心:value 是定长字节串,PFADD 不会让 key 长大,key 数乘以 12 KB 就是内存上限,不存在"某个 key 突然膨胀"的意外。

常见坑 / 最佳实践

  1. 不要把 PFCOUNT 当纯只读命令放进高频路径。 单 key 会缓存结果并可能触发编码转换,多 key 现场合并开销大,需要高频展示时用定时任务预计算落到普通 String。
  2. 口径统一先行。 用 uid 还是 IP 加 UA 作为去重元素,各业务方必须对齐,否则"同一个 UV"在不同系统里数字对不上。
  3. 对外的 HLL 数字要标注"估计值"。 0.81% 是标准差不是上限,用于考核或结算时必须事先声明,避免事后扯皮。
  4. HLL 无法删除单个元素。 脏数据只能 DEL 重建,写入侧要做好元素清洗(比如过滤爬虫 ID 后再 PFADD)。
  5. 按天建 key 加 PFMERGE 聚合,不要做一个无限追加的大 key。 大 key 无法按周期过期,也无法灵活回答任意时间段的问题。

小结

  • HLL 用固定约 12 KB 内存估计任意大基数,标准误差约 0.81%,由 16384 个 6 bit 桶决定。
  • PFADD 写入、PFCOUNT 读取(带写副作用)、PFMERGE 合并,按天建 key 聚合是标准结构。
  • 0.81% 是标准差,绝对误差随基数增长;HLL 永远是估计值,不能用于对账结算。
  • 要精确选 Set 或 Bitmap,只要数量选 HLL;无法删除、无法判存是它的硬边界。
【声明】本内容来自华为云开发者社区博主,不代表华为云及华为云开发者社区的观点和立场。转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息,否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0)

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。