使用Redis统计大量用户的唯一访问量(UV),最优方案是采用**HyperLogLog(HLL)**数据结构。
1. HyperLogLog 核心操作
- 添加用户标识:使用
PFADD
命令将用户唯一标识(如用户ID、IP)添加到HLL中。PFADD uv:20231001 "user_id_or_ip"
- 统计UV:使用
PFCOUNT
获取基数估计值。PFCOUNT uv:20231001
- 合并数据:使用
PFMERGE
合并多个HLL键(如多天合并为周/月统计)。PFMERGE uv:week_45 uv:20231001 uv:20231002 ... uv:20231007 PFCOUNT uv:week_45
2. 时间维度统计
- 按天存储:为每天创建独立键,如
uv:YYYYMMDD
。 - 设置过期时间:自动清理旧数据(例如保留7天):
建议在首次创建键时通过Lua脚本原子化操作:EXPIRE uv:20231001 604800 # 7天后过期
EVAL "redis.call('PFADD', KEYS[1], ARGV[1]); if redis.call('TTL', KEYS[1]) == -1 then redis.call('EXPIRE', KEYS[1], ARGV[2]) end; return 1;" 1 uv:20231001 "user123" 86400
3. 误差与性能
- 误差率:约0.81%,适用于近似统计场景。
- 内存占用:每个HLL键仅需约12KB,百万级UV仅需MB级内存。
4. 精确统计替代方案
若需精确UV,可选以下方法(根据场景权衡):
- 位图(Bitmap):用户ID为连续整数时高效。
SETBIT uv:20231001 100000 1 # 用户ID=100000访问 BITCOUNT uv:20231001 # 统计总数
- 集合(Set):小数据量时直接存储。
SADD uv:20231001 "user_id" SCARD uv:20231001
- 布隆过滤器 + 计数:需额外维护计数。
5. 分维度统计
在键名中加入业务标识,如页面或产品:
PFADD uv:homepage:20231001 "user_id"
PFCOUNT uv:homepage:20231001
总结
- 推荐方案:使用HyperLogLog实现低内存消耗的近似UV统计。
- 适用场景:允许误差的实时或大数据量统计(如DAU、活动参与数)。
- 注意事项:合理设置键过期时间,避免内存累积;合并数据时直接使用
PFMERGE
。
通过上述方法,Redis能够高效处理海量用户的UV统计需求,平衡内存占用与性能。