一,HpyerLogLog
Redis 的 HyperLogLog(HLL) 是一种概率型数据结构,用于高效地 统计大规模数据的唯一元素数量(基数估算)。它的核心特点是 占用固定极小内存(12KB),却能统计上亿级不重复元素,误差率仅约 0.81%。
HyperLogLog 的核心特性
| 特性 | 说明 |
|---|---|
| 极低内存占用 | 无论统计多少元素,固定占用 12KB 内存(可配置更小) |
| 高效去重计数 | 计算基数(唯一值数量)的时间复杂度 O(1) |
| 可接受误差率 | 标准误差 0.81%(实际误差通常更低) |
| 支持合并操作 | 多个 HLL 可合并,适合分布式统计 |
HyperLogLog 的底层原理
- 哈希函数
每个元素通过哈希函数转换为 64位二进制数,如
MurmurHash64。 - 分桶统计
- 将 64 位哈希值分为两部分:
- 前 14位 用于确定 桶编号(共 214=16384214=16384 个桶)
- 后 50位 用于计算 前导零的个数(从低位到高位)
- 每个桶记录当前最大前导零数量(ρρ)。
- 将 64 位哈希值分为两部分:
- 基数估算公式
使用 调和平均数 修正误差:E=α×m×m∑j=1m2−RjE=α×m×∑j=1m2−Rjm
- m: 桶数(16384)
- α: 修正常数(0.7213)
- Rj: 第 j个桶的前导零数
Redis 的 HyperLogLog 命令
-
添加元素
PFADD key element [element ...]-
示例:统计用户访问
PFADD visits:20231001 "user1" "user2" "user3"
-
-
统计基数
PFCOUNT key [key ...]-
示例:统计当日UV
PFCOUNT visits:20231001
-
-
合并多个 HLL
PFMERGE destkey sourcekey [sourcekey ...]-
示例:合并多天数据
PFMERGE visits:week1 visits:20231001 visits:20231002
-
二,实现UV统计
UV(Unique Visitor) 即 独立访客数,指在指定时间范围内访问某个系统的 去重用户数量。
- 核心特点:以用户为单位统计,同一用户多次访问只计1次。
- 对比PV:
- PV(Page View):页面浏览量(用户每次刷新计1次)
- UV ≠ PV:1个UV可能产生多个PV
示例:
- 用户A在一天内访问网站3次 → UV=1,PV=3
- 用户A和用户B各访问1次 → UV=2,PV=2
UV统计的核心挑战
- 海量数据去重
- 大型网站日均访问可能达亿级,需高效去重计数。
- 实时性要求
- 需要实时或近实时统计(如大屏展示)。
- 存储成本
- 精确统计(如用Set存储用户ID)消耗巨大内存。
我们直接利用单元测试,向HyperLogLog添加1百万数据,看看内存占用和统计效果
@Autowired
private StringRedisTemplate stringRedisTemplate;
@Test
public void testHyperLogLog(){
String[] values=new String[1000];
int j=0;
for (int i = 0; i < 1000000; i++) {
j=i%1000;
values[j]="user_"+i;
if(j==999){
//发送数据给redis
stringRedisTemplate.opsForHyperLogLog().add("hll",values);
}
}
//统计数量
Long count = stringRedisTemplate.opsForHyperLogLog().size("hll");
}