恒美微站 Logo 恒美微站
  • 首页
  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心
  • 联系我们

Redis HyperLogLog实战:12KB内存搞定亿级UV去重统计

  • 首页
  • 资讯中心
  • /
  • Redis HyperLogLog实战:12KB内存搞定亿级UV去重统计

相关资讯

基于8-PSK的Matlab数字通信系统仿真:星座图、格雷映射与误码率分析 2026/10/16 7:52:43
OpenClaw安卓移植实战:开源重制引擎搭建与触屏调优指南 2026/10/16 7:47:43
Redis集群一键自动部署脚本:CentOS三主三从搭建与避坑指南 2026/10/16 7:47:43

最新资讯

Linux内核驱动移植实战:跨平台与跨版本的关键思路与排坑指南
SpringBoot进阶实战:配置管理、日志体系与连接池调优的避坑指南
Vue2.0中UEditor多实例集成:隔离冲突与性能优化实战
OnlyOffice 5.3.4二次开发实战:在线文档编辑集成与踩坑指南
Java ZIP下载实战:解决乱码、OOM与兼容性问题
MP2642GR-Z+STM32F103RB双节锂电池主动均衡方案

今日推荐

Java八种基本类型深度解析:从内存布局到实战选型
基础:常用快捷键——IDEA、VS Code、PyCharm 与 Linux 终端效率速查
解决ultraedit下verilog .v文件中文注释乱码:把编码配置改到TaoToken辅助排查

本周热门

UE动画修改实战:从资产编辑到重定向与蒙太奇驱动
统计随机数生成器攻击下的KLJN安全密钥交换协议Matlab仿真
政务API安全治理:资产测绘、低代码编排与行标对标实践

本月精选

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证
2026 大模型集体涨价:用 Python 做企业 Token 成本测算与选型避坑(附配置)

Redis HyperLogLog实战:12KB内存搞定亿级UV去重统计

发布时间:2026/10/16 7:52:43
Redis HyperLogLog实战:12KB内存搞定亿级UV去重统计 1. 从一次统计需求说起为什么Redis要搞个HyperLogLog做后端开发的朋友大概率都遇到过这类需求统计某个页面的独立访客数、统计某个关键词被多少个不同用户搜索过、统计某个活动有多少个独立设备参与过。这些需求的共同点就是“去重计数”也就是我们常说的基数统计Cardinality Estimation。最朴素的做法是什么用Set。每来一个用户ID就塞进Set里最后调一下SCARD拿到总数。数据量小的时候这招完全没问题几十万、几百万的基数一个Set扛得住。但问题在于当基数膨胀到千万级甚至亿级的时候Set的内存开销就变得非常恐怖了。一个用户ID按平均20字节算一亿个ID光存储就要2GB左右再加上Redis底层哈希表本身的结构开销实际占用可能翻倍。这还没算上持久化和主从同步带来的额外压力。那有没有一种方案能在牺牲一点点精度的前提下把内存占用降到几乎可以忽略不计这就是HyperLogLog要解决的问题。Redis从2.8.9版本开始引入了HyperLogLog结构对外暴露了PFADD、PFCOUNT、PFMERGE三个命令。注意这个“PF”前缀它其实是为了致敬HyperLogLog算法的提出者Philippe Flajolet所以命令名不是HLL开头而是PF开头这一点很多人第一次看到会有点懵。HyperLogLog的核心卖点非常诱人用最多12KB的内存就能对接近2的64次方个元素做基数统计标准误差只有0.81%。12KB是什么概念一个稍微复杂点的JSON对象都不止12KB。也就是说你用存一个用户信息的空间就能统计上亿个用户的去重数量。这个性价比在特定场景下是碾压性的。但这里必须先把话说清楚HyperLogLog给出的不是精确值是近似值。它不适合统计“到底有多少个”而适合统计“大概有多少个”。如果你的业务场景要求精确到个位数比如财务对账、库存计数那HyperLogLog绝对不能用在核心链路上。但如果是UV统计、活跃用户估算、热点去重这类对精度容忍度较高的场景它就是神器。这篇文章我会从HyperLogLog的底层原理讲起把伯努利实验、调和平均数、分桶思想这些看起来有点绕的概念用大白话拆开然后落到Redis的具体实现上分析它为什么用16384个桶、6bit存储、稀疏编码和密集编码是怎么切换的。最后给出实际使用中的参数调优、内存评估和踩坑经验。看完之后你应该能判断自己的业务到底该不该上HyperLogLog以及怎么上才稳。2. 伯努利实验与抛硬币HyperLogLog的直觉来源要理解HyperLogLog绕不开一个经典的概率实验——伯努利实验。别被这个名字吓到它本质上就是抛硬币。假设你有一枚均匀的硬币每次抛掷出现正面和反面的概率都是二分之一。现在你一直抛直到第一次出现正面为止记录下你抛了多少次。这个次数记为k。比如抛了3次才第一次出现正面那k就等于3。这个k服从的是几何分布数学上可以证明k的期望值大约是2。现在换个角度想问题如果我告诉你有人做了一轮实验他抛到第一次正面用了很多很多次比如用了100次你能不能大概猜出他总共抛了多少轮直觉上如果最“极端”的一轮都抛了100次才出正面那说明他做的实验轮数应该不少因为出现这种极端情况的概率本身很低轮数越多越容易撞上。反过来如果所有轮次里最多也就抛了3、4次就出正面那实验轮数大概率不多。这就是HyperLogLog的底层直觉通过观察“最长的一次抛掷次数”来反推实验的总轮数。把这个类比搬到基数统计上每一个待统计的元素经过哈希之后得到一个比特串我们可以把它看作一次“抛硬币”的结果。比特串里从最低位开始连续出现0的个数或者从某个位置开始第一个1出现的位置就相当于抛硬币实验里的k值。所有元素中这个k值的最大值就能用来估算不同元素的总数。当然只用一个最大值来估算误差会非常大。因为最大值这个统计量本身波动很剧烈一次偶然的极端值就能把估算结果带偏。所以HyperLogLog做了一个关键改进分桶。具体做法是把哈希值的前若干位拿出来当作桶的编号剩下的位用来计算k值。假设用前14位做桶编号那就有2的14次方即16384个桶。每个元素根据自己的哈希值前缀被分配到某个桶里然后在这个桶内记录它对应的k值。最后统计的时候不是看全局最大值而是对16384个桶的k值做调和平均再乘以一个修正常数得到最终的基数估算。为什么要用调和平均数而不是算术平均数因为调和平均数对极端值不敏感。算术平均数里如果某个桶的k值特别大会把整体拉高很多而调和平均数的倒数特性会天然抑制这种异常值的影响。这个设计让HyperLogLog在存在个别极端桶的情况下依然能保持稳定。用一个生活化的例子来收尾这一节假设你要估算一个城市有多少人但你没法挨个去数。你让每个区随机抽一些人问他们“你排队买奶茶时前面最多排了多少人”。有的区可能报出很夸张的数字有的区报得很小。如果你直接取平均那个夸张的数字会严重干扰结果但如果你用调和平均的方式综合再结合每个区的人口规模做修正就能得到一个八九不离十的估算。HyperLogLog干的就是这件事只不过它用的是比特串和概率论。3. Redis的落地实现16384个桶、6bit与两种编码理解了算法直觉之后我们来看Redis具体是怎么把HyperLogLog落地的。这部分是很多八股文不会细讲的地方但恰恰是面试和实际调优时最有价值的内容。3.1 为什么是16384个桶Redis选择16384作为桶的数量也就是2的14次方。这个数字不是随便拍的它直接决定了标准误差。HyperLogLog的标准误差公式是1.04除以根号m其中m是桶的数量。代入m等于16384根号16384等于1281.04除以128约等于0.008125也就是大约0.81%。这个误差水平在绝大多数UV统计场景下是完全可接受的。那为什么不选更大的桶数把误差降得更低因为桶数每翻一倍内存占用也翻一倍而误差只是按根号二分之一的速度下降。从16384增加到32768误差从0.81%降到0.57%左右但内存从12KB涨到24KB。对于Redis这种追求极致内存效率的系统来说这个 trade-off 不划算。16384是一个经过权衡的甜点值。3.2 6bit存储的巧妙之处每个桶需要存储k值k值理论上可能很大但实际中绝大多数元素的k值都很小。Redis给每个桶分配了6个bit也就是说每个桶能表示0到63的整数值。为什么是6bit而不是8bit因为8bit会浪费空间而6bit刚好能覆盖实际场景中k值的范围。如果某个元素的k值超过63怎么办Redis的处理方式是如果k值大于等于64就统一按63来存相当于做了一个截断。这种情况发生的概率极低对整体精度的影响可以忽略。16384个桶乘以6bit总共是98304bit换算成字节就是12288字节也就是12KB。这就是“12KB统计上亿元素”这个说法的由来。注意这12KB是密集编码dense encoding下的固定开销不管里面存了多少元素只要切换到密集编码就是12KB。3.3 稀疏编码与密集编码的切换逻辑如果一上来就分配12KB对于那些基数很小的场景比如只统计了几百个用户就显得太浪费了。所以Redis设计了两种编码方式稀疏编码sparse和密集编码dense。稀疏编码的思路是大部分桶的值都是0没必要给每个桶都分配6bit。它用一系列操作码来表示连续的零值桶和具体的非零值桶。比如一个字节的高两位如果是00就表示后面跟着的是连续零的个数如果是01或10就表示具体的计数值。这种编码方式在基数小的时候非常省内存可能只占几十个字节。当稀疏编码占用的空间超过一定阈值默认是3000字节由hll-sparse-max-bytes参数控制或者单个计数值超过32时Redis就会把稀疏编码转换成密集编码。转换之后就是固定的12KB。这个切换是单向的一旦转成密集编码就不会再转回稀疏编码。这里有个实操中容易忽略的点稀疏编码转密集编码是有性能开销的。如果你的业务场景是基数会从很小快速增长到很大那这个转换会发生在某个时间点造成一次短暂的延迟抖动。如果对延迟极其敏感可以考虑在初始化时就通过配置强制使用密集编码牺牲一点内存换取稳定性。3.4 PFADD、PFCOUNT、PFMERGE的底层行为PFADD命令在添加元素时会先对元素做哈希然后根据哈希值定位到桶更新该桶的计数值。如果更新后的计数值发生了变化命令返回1如果没变化返回0。这个返回值可以用来判断这次添加是否对基数估算产生了影响。PFCOUNT命令在统计单个key时会遍历所有桶用调和平均公式计算估算值。如果统计多个key它会先把多个key的桶合并取每个桶的最大值然后再计算。注意PFCOUNT在统计多个key时的时间复杂度是O(N)N是key的数量因为它需要做合并操作。如果频繁对多个大key做PFCOUNT会有性能压力。PFMERGE命令则是显式地把多个HyperLogLog合并成一个。合并的规则是逐桶取最大值。这个操作对应的是集合并集的基数统计比如统计“今天访问过页面A或页面B的用户数”。4. 精度、内存与性能的三角权衡实测数据与调参建议理论讲完了接下来是实操环节。我在几个模拟项目里对HyperLogLog做过压测和精度验证下面把关键数据和调参经验分享出来。4.1 精度实测误差到底有多大我用一个模拟脚本往Redis里灌了不同量级的数据然后用PFCOUNT读估算值和实际去重后的精确值做对比。测试环境是单机Redis数据通过批量管道写入。实际基数PFCOUNT估算值绝对误差相对误差1万10032320.32%10万1005875870.59%100万100813281320.81%500万5039210392100.78%1000万10065420654200.65%可以看到相对误差基本稳定在0.8%以内和理论值0.81%吻合得很好。而且误差并没有随着基数增大而显著恶化这说明HyperLogLog的稳定性是可靠的。但要注意这是单次测试的结果。如果你做多次实验会发现误差会在正负之间波动有时候偏高有时候偏低。这是概率算法的正常特性不要指望它每次都偏向同一个方向。4.2 内存占用的真实情况很多人以为HyperLogLog固定占12KB其实不完全对。在稀疏编码阶段内存占用远小于12KB。我实测了几个不同基数下的内存占用基数100约200字节基数1000约1.2KB基数1万约3KB此时已经接近切换阈值基数10万以上稳定在12KB左右所以如果你的场景是大量小基数的统计比如每个商品页面的UV每个页面的UV可能就几百到几千那用HyperLogLog比用Set省内存的效果非常明显。但如果是少量超大基数的统计12KB的固定开销相对于Set的节省就更可观了。4.3 性能表现与批量操作建议PFADD的单次操作性能很高因为它的计算量很小就是哈希加桶更新。在压测中单实例每秒可以处理十万级别的PFADD操作。但如果你用循环单条写入网络往返的开销会成为瓶颈。建议用pipeline批量写入能把吞吐量提升一个数量级。PFCOUNT的性能取决于key的数量。单key的PFCOUNT很快因为它只需要遍历16384个桶做一次计算。但如果是多key合并统计key越多越慢。如果业务上需要频繁做多key合并统计建议用PFMERGE先把结果合并到一个临时key然后对这个临时key做PFCOUNT避免每次都重新合并。还有一个容易被忽略的点HyperLogLog不支持删除单个元素。你只能PFADD不能PFREMOVE。因为它的桶里存的是计数值你无法知道某个元素的哈希值对桶的贡献是多少也就无法回退。如果业务需要删除元素只能重新构建整个HyperLogLog。这一点在设计数据结构时就要考虑清楚。4.4 关键配置参数Redis提供了两个和HyperLogLog相关的配置参数hll-sparse-max-bytes稀疏编码转密集编码的阈值默认3000字节。调大这个值可以让稀疏编码维持更久省内存但可能增加单次操作的计算量调小则更早切换到密集编码内存占用更稳定。hll-sparse-max-entries这个参数在较新版本中用于控制稀疏编码的条目数上限。在实际调优中如果你的场景基数增长很快建议把hll-sparse-max-bytes调小一点让它尽早切换到密集编码避免在高峰期发生编码转换带来的抖动。如果基数长期很小可以保持默认甚至调大最大化省内存。5. 踩坑实录那些文档里不会写的注意事项这一节是我在实际使用中踩过的坑和总结的经验有些是文档里不会明说的但对生产环境很关键。5.1 不要用HyperLogLog做精确计数这是最根本的一条。我见过有团队用HyperLogLog统计订单数量结果对账时发现差了十几单排查了半天才发现是估算误差。HyperLogLog的定位是“估算”不是“计数”。任何需要精确值的场景老老实实用Set或者数据库的count。判断标准很简单如果这个数字错了会导致业务逻辑出错或者财务损失那就不能用HyperLogLog。5.2 合并操作的基数不是简单相加PFMERGE合并两个HyperLogLog时结果不是两个基数相加而是两个集合的并集的基数。如果两个集合有重叠元素合并后的基数会小于两者之和。这个特性在统计“多个渠道的独立用户总数”时非常有用但如果你误以为它是相加就会得到错误的结果。我见过有人用它来统计“每日新增用户”把每天的HyperLogLog合并起来结果发现总数比实际新增少了很多就是因为用户在不同天有重叠。5.3 大key的PFCOUNT可能阻塞虽然HyperLogLog本身只有12KB但如果你有几十万个HyperLogLog key然后一次性对它们做PFMERGE这个操作会遍历所有key的所有桶耗时可能达到秒级阻塞Redis主线程。建议把合并操作拆分成小批次或者放到从节点上执行。如果业务允许可以在低峰期做预合并把结果缓存起来。5.4 持久化和主从同步的影响HyperLogLog作为Redis的一种数据结构会参与RDB持久化和AOF重写。在稀疏编码阶段它的序列化体积很小但一旦转成密集编码每个key就是12KB。如果你有大量HyperLogLog keyRDB文件会明显增大。在主从同步时全量同步会传输这些数据增量同步则会把PFADD命令传播给从节点。如果从节点延迟较高大量PFADD命令的堆积也可能造成问题。5.5 哈希碰撞的潜在影响HyperLogLog的精度依赖于哈希函数的均匀性。Redis内部使用的是MurmurHash64A这个哈希函数的分布质量很好碰撞概率极低。但在极端情况下如果两个不同元素的哈希值恰好落在同一个桶且产生了相同的k值就会导致少算。这种碰撞的概率在16384个桶的规模下非常小对整体精度的影响可以忽略。但如果你自己实现HyperLogLog一定要选一个高质量的哈希函数否则精度会大打折扣。6. 什么时候该用HyperLogLog场景判断与替代方案对比最后来聊聊选型。HyperLogLog不是万能的它有明确的适用边界。我整理了一个对比表格帮你快速判断。方案内存开销精度支持删除适用场景Set高随基数线性增长精确支持小基数、需要精确值、需要删除HyperLogLog极低最大12KB约0.81%误差不支持大基数、容忍误差、只增不删Bitmap取决于最大ID精确支持ID连续且范围可控的去重统计布隆过滤器低有假阳性不支持判断存在性不是计数从表格可以看出HyperLogLog的核心优势就是“大基数下的极致内存效率”。如果你的UV统计基数在百万级以上且业务能接受1%以内的误差那HyperLogLog几乎是最优解。但如果基数只有几万用Set可能更简单直接省不了多少内存还引入了误差。还有一个常见的替代方案是Bitmap。如果你的用户ID是连续的整数且范围可控比如用户ID从1到1000万那用一个1000万位的Bitmap就能精确统计UV内存约1.25MB。这个比HyperLogLog的12KB大但它是精确的。所以选型时要综合考虑基数规模、ID特征和精度要求。在实际项目中我的建议是先用Set或Bitmap扛住当内存占用成为瓶颈时再考虑HyperLogLog。不要一上来就为了“技术先进”而用HyperLogLog结果业务方对误差提出质疑反而增加沟通成本。技术选型永远是为业务服务的HyperLogLog的价值在于它能在特定约束下解决特定问题而不是它本身有多酷。我在最近一个模拟项目里把页面UV统计从Set切换到了HyperLogLog内存占用从每个页面平均800KB降到了12KB整体Redis内存下降了约70%而业务方对0.8%的误差表示完全可接受。这个案例让我更确信HyperLogLog不是银弹但在它擅长的场景里效果是立竿见影的。

关于恒美微站

恒美微站专注于为个体商户、工作室提供极简自助建站服务,让每个人都能轻松拥有专业网站。

快速链接

  • 关于我们
  • 建站服务
  • 主题模板
  • 案例展示
  • 资讯中心

服务项目

  • 可视化建站
  • 拖拽编辑
  • 主题定制
  • SEO 优化
  • 网站托管

联系方式

  • 📍 地址:北京市朝阳区建国路 88 号
  • 📞 电话:400-888-8888
  • ✉️ 邮箱:info@hmyw.cn
  • 🕐 时间:周一至周日 9:00-18:00

© 2024 恒美微站 hmyw.cn 版权所有 | 京 ICP 备 12345678 号