十,Redis通过HyperLogLog实现UV统计

267 阅读3分钟

一,HpyerLogLog

Redis 的 HyperLogLog(HLL) 是一种概率型数据结构,用于高效地 统计大规模数据的唯一元素数量(基数估算)。它的核心特点是 占用固定极小内存(12KB),却能统计上亿级不重复元素,误差率仅约 0.81%

HyperLogLog 的核心特性

特性说明
极低内存占用无论统计多少元素,固定占用 12KB 内存(可配置更小)
高效去重计数计算基数(唯一值数量)的时间复杂度 O(1)
可接受误差率标准误差 0.81%(实际误差通常更低)
支持合并操作多个 HLL 可合并,适合分布式统计

HyperLogLog 的底层原理

  1. 哈希函数 每个元素通过哈希函数转换为 64位二进制数,如 MurmurHash64
  2. 分桶统计
    • 将 64 位哈希值分为两部分:
      • 14位 用于确定 桶编号(共 214=16384214=16384 个桶)
      • 50位 用于计算 前导零的个数(从低位到高位)
    • 每个桶记录当前最大前导零数量(ρρ)。
  3. 基数估算公式 使用 调和平均数 修正误差:E=α×m×m∑j=1m2−RjE=α×m×∑j=1m2−Rjm
    • m: 桶数(16384)
    • α: 修正常数(0.7213)
    • Rj: 第 j个桶的前导零数

Redis 的 HyperLogLog 命令

  1. 添加元素

    PFADD key element [element ...]
    
    • 示例:统计用户访问

      PFADD visits:20231001 "user1" "user2" "user3"
      
  2. 统计基数

    PFCOUNT key [key ...]
    
    • 示例:统计当日UV

      PFCOUNT visits:20231001
      
  3. 合并多个 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统计的核心挑战

  1. 海量数据去重
    • 大型网站日均访问可能达亿级,需高效去重计数。
  2. 实时性要求
    • 需要实时或近实时统计(如大屏展示)。
  3. 存储成本
    • 精确统计(如用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");

    }