Redis HyperLogLog 完全攻略
“极致省内存的基数统计神器”
12KB 内存,统计 10 亿级不重复元素,误差 < 1%
完美替代Set做 UV、IP 去重、用户行为分析!
一、HyperLogLog 核心特点
| 特性 | 说明 |
|---|---|
| 用途 | 基数统计(Cardinality) —— 统计不重复元素数量 |
| 内存固定 | ≈ 12 KB,无论统计 1 个还是 10 亿 |
| 误差率 | ≈ 0.81%(标准误差) |
| 不支持删除 | 只能添加,不能 DEL 单个元素 |
| 命令 | 仅 PFADD、PFCOUNT、PFMERGE |
| 内部结构 | 基于 HyperLogLog 算法 + 稀疏/密集存储优化 |
二、HyperLogLog 命令全表(仅 3 个!)
| 命令 | 说明 | 示例 |
|---|---|---|
PFADD key element [element ...] | 添加元素 | PFADD page:uv "u1" "u2" "u1" |
PFCOUNT key [key ...] | 统计基数 | PFCOUNT page:uv → 2 |
PFMERGE destkey sourcekey [sourcekey ...] | 合并多个 HLL | PFMERGE all:uv day1 day2 |
PF = Probabilistic Filter(概率过滤器)
三、核心实战场景
1. 网站日 UV 统计(百万级用户)
# 用户访问首页
PFADD uv:20251112:home "user:1001" "user:1002" "user:1001"
# 获取今日 UV
PFCOUNT uv:20251112:home → 2
# 保留 30 天
EXPIRE uv:20251112:home 2592000
10亿用户 UV → 仅 12KB 内存!
2. 多日 UV 合并(周/月 UV)
# 每天统计
PFADD uv:20251112 "u1" "u2"
PFADD uv:20251113 "u2" "u3"
PFADD uv:20251114 "u3" "u4"
# 合并本周 UV
PFMERGE uv:week:202511 "uv:20251112" "uv:20251113" "uv:20251114"
PFCOUNT uv:week:202511 → 4
3. 文章独立访问用户数
PFADD article:1001:uv "u1" "u2" "u1" "u3"
PFCOUNT article:1001:uv → 3
4. 实时去重 IP 统计
PFADD ip:daily "192.168.1.1" "192.168.1.2" "192.168.1.1"
PFCOUNT ip:daily → 2
5. 用户行为路径去重(A→B→C)
PFADD path:login_to_pay "u1" "u2" "u1"
PFCOUNT path:login_to_pay
四、HyperLogLog vs Set 对比(UV 统计)
| 对比项 | HyperLogLog | Set |
|---|---|---|
| 内存占用 | ≈12KB | N × 64 字节 |
| 10万 UV | 12KB | ≈6.4MB |
| 1000万 UV | 12KB | ≈640MB |
| 支持删除 | 不支持 | 支持 |
| 精确度 | ≈99.19% | 100% |
| 适用场景 | 大基数、不需精确 | 小基数、需精确 |
结论:UV > 10万,用 HLL!
五、误差分析与精度控制
| 统计数量 | 标准误差 | 实际误差范围 |
|---|---|---|
| 10,000 | ±1.64% | ±164 人 |
| 100,000 | ±0.81% | ±810 人 |
| 1,000,000 | ±0.26% | ±2,600 人 |
| 10,000,000 | ±0.081% | ±8,100 人 |
误差可接受?99.9% 的业务都 OK!
六、内存与性能优化建议
| 建议 | 说明 |
|---|---|
| 每天一个 key | uv:20251112 → 方便合并、过期 |
| 定期清理 | EXPIRE 自动删除 |
| 合并前评估 | PFMERGE 前确认必要性 |
| 避免滥用 | 小数据用 Set 更精确 |
# 自动清理脚本(每天 00:00)
EXPIRE uv:20251013 86400 # 保留30天
七、常见问题与解决方案
| 问题 | 原因 | 解决方案 |
|---|---|---|
| UV 统计偏低 | 元素未添加 | 确保每次访问都 PFADD |
| 内存泄漏 | key 未过期 | 加 EXPIRE |
| 不能删除用户 | HLL 不支持 | 只能重建 |
| 合并后 UV 爆炸 | 重复合并 | 控制合并频率 |
八、一键速查表
# 每日 UV
PFADD uv:20251112 "u1" "u2" "u1"
PFCOUNT uv:20251112
# 周 UV 合并
PFMERGE uv:week:202511 "uv:20251112" "uv:20251113"
PFCOUNT uv:week:202511
# 设置过期
EXPIRE uv:20251112 2592000 # 30天
九、客户端代码示例
Python (redis-py)
import redis
r = redis.Redis()
# 记录用户访问
r.pfadd('uv:20251112:home', 'user:1001', 'user:1002', 'user:1001')
# 获取 UV
uv = r.pfcount('uv:20251112:home')
print(f"今日 UV: {uv}")
# 合并周 UV
r.pfmerge('uv:week:202511', 'uv:20251112', 'uv:20251113')
week_uv = r.pfcount('uv:week:202511')
Go (go-redis)
ctx := context.Background()
rdb := redis.NewClient(&redis.Options{Addr: "localhost:6379"})
rdb.PFAdd(ctx, "uv:20251112", "u1", "u2")
uv, _ := rdb.PFCount(ctx, "uv:20251112").Result()
fmt.Println(uv) // 2
十、HyperLogLog 在高并发中的妙用
1. 实时大屏 UV 展示
# 每秒更新
PFADD uv:realtime "u1" "u2" ...
PFCOUNT uv:realtime
2. 多维度 UV 分析
PFADD uv:20251112:mobile "u1" "u3"
PFADD uv:20251112:pc "u2" "u4"
PFCOUNT uv:20251112:mobile
3. 结合 Lua 实现原子统计
EVAL "
redis.call('pfadd', KEYS[1], ARGV[1])
return redis.call('pfcount', KEYS[1])
" 1 uv:20251112 "u1"
十一、HLL vs 其他去重方案
| 方案 | 内存 | 精度 | 删除 | 推荐场景 |
|---|---|---|---|---|
Set | 高 | 100% | 支持 | < 10万 |
HyperLogLog | 12KB | ≈99.2% | 不支持 | > 10万 |
Bitmap | 1bit/用户 | 100% | 支持 | 已知用户ID |
完成!你已精通 Redis HyperLogLog!
# 一行命令体验 HLL 全功能
redis-cli <<EOF
PFADD hll a b c a b
PFCOUNT hll
PFMERGE hll2 hll hll
PFCOUNT hll2
EXPIRE hll 86400
EOF
下一步推荐:
需要我送你:
- “亿级 UV 实时统计系统(HLL + 分片)”?
- “多维度用户行为分析平台”?
- “HLL + ClickHouse 离线分析方案”?
回复:亿级UV | 行为分析 | 离线方案 即可!