Redis统计独立用户访问量的四种方案
在网站分析、广告监测、推荐系统等场景中,独立用户访问量(UV,Unique Visitor)是一个核心指标。UV 的关键在于去重——同一个用户多次访问只计一次。
Redis 提供了多种数据结构来高效实现 UV 统计,各有优劣。本文将详细对比 Set、Bitmap、HyperLogLog、incr + 日期维度四种方案,并通过流程图和代码示例帮助你选型。
免费影视、动漫、音乐、游戏、小说资源长期稳定更新! 👉 点此立即查看 👈
一、方案概览(附选型流程图)

二、方案一:Set 集合(精确去重)
最直观的方法是什么?没错,就是为每个统计周期(比如一天)维护一个 Set,把每个访问过的用户 ID 都塞进去,最后用 SCARD 命令获取基数,搞定。
# 示例:用户 1001 访问首页
redis.sadd("uv:home:2025-04-15", "user_1001")
# 获取当天 UV
uv = redis.scard("uv:home:2025-04-15")
优点:结果绝对精确,而且对用户 ID 的类型非常宽容,字符串或整数都行。
缺点:内存占用是个硬伤。每个用户 ID 都需要完整存储一份。简单算笔账:1000 万用户,每个 ID 按 30 字节算,就得吃掉约 300MB 内存。
适用场景:用户量不大(百万级以下),或者对统计精度有严苛要求的场景。
三、方案二:Bitmap(位图法,精确且内存极省)
如果你的用户 ID 是整数,并且相对连续(比如常见的自增 user_id),那么 Bitmap 就是为你量身定做的神器。它的思路很巧妙:把每个 user_id 直接映射到一个位偏移量上,用户访问了,就把对应的位设为 1。
# 用户 ID=1001 访问,设置第 1001 位为 1
redis.setbit("uv:home:2025-04-15", 1001, 1)
# 统计当日 UV(统计 1 的个数)
uv = redis.bitcount("uv:home:2025-04-15")
内存计算:优势就在这里。假设有 1 亿用户,只需要 1亿 bit ≈ 12 MB 内存,比 Set 方案节省了数十倍。
优点:精确、内存消耗极小、性能还高(虽然 bitcount 理论复杂度是 O(n),但 Redis 底层做了大量优化)。
缺点:限制也很明显。用户 ID 必须是非负整数,而且不能太稀疏。举个例子,如果最大用户 ID 是 10 亿,但实际只有 100 万用户,Bitmap 依然会预先分配 125MB 的连续空间,这就造成了浪费。
适用场景:用户 ID 是自增整数、最大 ID 可控(比如在 2^32 以内)、对内存敏感同时又要求精确统计的场景。
四、方案三:HyperLogLog(近似去重,误差 0.81%)
接下来聊聊 HyperLogLog,这是一种概率性数据结构。它最吸引人的地方在于,只用固定的 12KB 内存,就能统计上亿级别的 UV,代价是存在约 0.81% 的误差。
# 添加元素
redis.pfadd("uv:home:2025-04-15", "user_1001", "user_1002")
# 获取近似 UV
uv = redis.pfcount("uv:home:2025-04-15")
原理简述:它通过哈希函数将元素映射成二进制串,然后观察低位连续零的个数来估计基数,是一种用精度换空间的经典策略。
优点:内存占用固定且极小(12KB),添加和统计性能都是 O(1),非常适合海量数据场景。
缺点:结果不精确;无法回溯具体有哪些用户访问过;因此,在涉及计费、对账等数据敏感的场景中需要慎用。
适用场景:大屏实时展示、趋势分析、非精准的营销活动统计等可以容忍一定误差的场景。
五、方案四:incr + 日期维度(你提到的“incr自增”)
这里需要特别澄清一下。严格来说,单纯使用 INCR 命令无法实现独立用户去重。因为 INCR 是一个纯粹的累加计数器,每次访问都会 +1,得到的是 PV(页面访问量),而不是 UV。
# 这样得到的是 PV,不是 UV
redis.incr("pv:home:2025-04-15")
那么,incr 如何辅助 UV 统计呢?
常见的做法是组合使用:
- 用 Set、Bitmap 或 HLL 来存储独立用户标识,确保去重。
- 同时,用 incr 来记录总访问次数(PV)。
# 记录 PV
redis.incr("pv:home:2025-04-15")
# 记录 UV(使用 HLL)
redis.pfadd("uv:home:2025-04-15", user_id)
所以,原文中提到的“incr 通过自增方式判断用户的访问量”更适用于 PV 统计。为了表述准确,我们修正一下:incr 适合统计 PV,而 UV 必须依赖具备去重能力的数据结构。
六、四种方案对比表
| 方案 | 内存占用 | 精确性 | 支持用户ID类型 | 时间复杂度(写入) | 典型应用 |
|---|---|---|---|---|---|
| Set | O(N)(每个元素完整存储) | 精确 | 任意 | O(1) | 小规模精确统计 |
| Bitmap | O(max_id) 位,连续整数时极省 | 精确 | 非负整数 | O(1) | 亿级整数ID,如手机号后几位 |
| HyperLogLog | 固定 12KB | 近似(误差 0.81%) | 任意(需哈希) | O(1) | 海量UV快速估算 |
| incr(PV) | 固定(每个key一个整数) | 精确 | 无(只是计数) | O(1) | 页面访问总量(非UV) |
七、实战选型建议
情况一:你的用户 ID 是整数且密集(如 user_id 从 1 到 5000 万)
? 首选 Bitmap,精确且内存最小。
情况二:用户 ID 是字符串(如 UUID、手机号),且允许 0.81% 误差
? 首选 HyperLogLog,12KB 内存统计上亿 UV,性价比之王。
情况三:必须精确统计,且用户量较小(< 500 万)
? 用 Set,简单可靠,心智负担低。
情况四:既要 PV 又要 UV
? 组合拳:INCR 记录 PV + PFADD 记录 UV(HLL)或 SADD(Set)。
情况五:数据敏感场景(如计费、反作弊)
❌ 坚决不能用 HyperLogLog,必须选用 Bitmap 或 Set 来保证精确性。
八、代码示例:三种方案对比(Python + Redis)
import redis
r = redis.Redis(decode_responses=True)
# 模拟 100 万个用户 ID(字符串)
user_ids = [f"user_{i}" for i in range(1_000_000)]
# 1. Set 方式
key_set = "uv:set"
r.delete(key_set)
for uid in user_ids:
r.sadd(key_set, uid)
print(f"Set 精确 UV: {r.scard(key_set)}")
print(f"Set 内存: {r.memory_usage(key_set) / 1024 / 1024:.2f} MB")
# 2. HyperLogLog 方式
key_hll = "uv:hll"
r.delete(key_hll)
for uid in user_ids:
r.pfadd(key_hll, uid)
print(f"HLL 近似 UV: {r.pfcount(key_hll)}")
print(f"HLL 内存: {r.memory_usage(key_hll)} 字节") # 固定约 12KB
# 3. Bitmap 方式(假设 user_id 转为整数,此处用 i 模拟)
key_bit = "uv:bitmap"
r.delete(key_bit)
for i in range(1, 1_000_001):
r.setbit(key_bit, i, 1)
print(f"Bitmap 精确 UV: {r.bitcount(key_bit)}")
print(f"Bitmap 内存: {r.memory_usage(key_bit) / 1024 / 1024:.2f} MB")
运行结果参考(百万级用户):
- Set:内存约 30~40 MB
- HLL:12 KB
- Bitmap:0.12 MB(100 万 bit = 0.125 MB)
数据不会说谎,内存消耗的差距一目了然。
九、总结
| 你的原始说法 | 修正/补充 |
|---|---|
| “incr 通过自增方式判断用户的访问量” | incr 得到的是 PV(总访问次数),不是 UV。UV 需要去重。 |
| “HyperLogLog 用来做基数统计,误差很小,不适合数据敏感场景” | ✅ 正确。误差约 0.81%,内存固定 12KB,适合海量近似统计。 |
最终结论:
- 对精度要求不高、数据量极大 → HyperLogLog
- 需要精确、用户 ID 为整数 → Bitmap
- 需要精确、用户 ID 为字符串且量小 → Set
- 想要统计 PV → incr
说到底,技术选型没有银弹,关键在于理解业务场景的真实需求。合理选择数据结构,能让你的 UV 统计既快又省内存,游刃有余。
以上就是Redis统计独立用户访问量的四种方案的详细内容,更多关于Redis统计独立用户访问量的资料请关注本站其它相关文章!
您可能感兴趣的文章:- Redis统计访问量的3种实现方式
- Redis如何统计用户访问量
- SpringBoot整合Redis实现访问量统计的示例代码
热门专题
热门推荐
创意工坊也“宽”起来了:Steam最新界面改革进入测试 看来,Steam这股“加宽”的势头是停不下来了。继商店页面拓宽和首页开启宽屏测试之后,Valve这次把目光投向了玩家们再熟悉不过的创意工坊。最近,一项旨在让浏览体验“更迅速、更易用”的界面革新,已经正式启动了Beta测试。 根据官方消息,想要抢
《战争机器:事变日》重磅回归:一场回归纯粹恐怖的生存之旅 近日,游戏界传来重磅消息。据Playground Games官方透露,微软Xbox旗下的经典IP《战争机器》系列,即将推出一部风格彻底转型的新作——《战争机器:事变日》。本作的核心开发理念十分明确:摒弃近年来系列作品中常见的“超级英雄”式叙事
一、安币官网核心入口解析 接触一个平台,第一步走对至关重要。官方网站,就是那个最权威、最核心的入口。它不仅是获取信息的第一站,更是所有账户管理和交易操作的基石。通过官网访问,能有效避开那些精心伪装的仿冒网站,这是守护资产安全的第一道,也是最重要的一道防线。 那么,如何找到真正的官网?通过可靠的搜索引
iPhone开机只显示低电量图标后黑屏?别慌,这是“虚电”在作祟 遇到iPhone开机,屏幕只闪一下低电量图标就彻底黑屏,或者插上充电器半天都没反应?先别急着断定是主板坏了。这种情况,十有八九是电池老化导致的“虚电”现象在捣鬼——系统以为还有电,实际上电池的供电能力早已力不从心。下面这套从易到难的排
一、通过“显示与亮度”常规路径设置 这个方法最基础,也最稳妥。无论你的iPhone是什么系统版本,在“设置”里都能找到它。本质上,它就是直接调整系统判定屏幕“闲置”的那个时间阈值——一旦超过这个时长没有任何操作,屏幕就会自动熄灭。 操作起来很简单,就四步: 1 在主屏幕找到那个齿轮状的设置应用,点





