使用Redis的HyperLogLog进行海量数据统计

一、概述:

Redis的HyperLogLog(HyperLogLog)是一种用于估算集合中不重复元素数量的数据结构。它是Redis的一个流行功能,经常用于处理大型数据集的基数估计(即不重复元素的数量)。

HyperLogLog的关键思想是使用一段随机二进制码序列的长度来估计输入集合中不重复元素数量的近似值。这个过程分为两个步骤:

  1. 散列(Hash):将每个元素转换为4字节的哈希值。这是通过将元素的每个比特位都转换为0或1,然后对2的幂进行取模来实现的。这个哈希值用于确定该元素在HyperLogLog中的位置。
  2. 位运算(Bitwise operations):对于HyperLogLog中的每个位置,记录其对应的二进制序列中1的个数。这些1的个数组成了估计基数的基本HyperLogLog结构。

当一个新元素被添加到HyperLogLog时,它的哈希值被计算并用于确定其在HyperLogLog中的位置。然后,该位置的二进制序列中1的个数减去这个新元素对应序列中1的个数,然后将结果与基本HyperLogLog中记录的所有1的个数求和。这个过程重复进行,直到所有元素都被处理完毕。

HyperLogLog的主要优点是空间效率高和计算速度快。由于它们只使用4字节来估计数百万个不重复元素,所以它们占用很小的内存。另外,位运算的操作速度非常快,可以在几微秒内处理大量数据。

尽管HyperLogLog是一种非常快速和准确的基数估计方法,但它只能给出近似值。实际精度取决于所使用的基数大小和HyperLogLog中的二进制序列中1的数量。然而,在大多数情况下,这种近似值已经足够接近实际值。

二、Redis的HyperLogLog常用命令:

Redis的HyperLogLog是Redis 3.2引入的一种数据结构,用于基数统计和集合去重。下面是Redis的HyperLogLog常用命令:

  • PFADD key element [element …]:向HyperLogLog中添加元素,多个元素之间可以用空格隔开。
127.0.0.1:6379> PFADD hll e1 e2 e3 e4 e5
(integer) 1
  • PFCOUNT key [key …]:返回HyperLogLog中元素的数量。如果提供了多个key,则对每个key的值求和。
127.0.0.1:6379> PFCOUNT hll
(integer) 5
  • PFMERGE dest key [key …]:将多个HyperLogLog合并成一个。如果两个HyperLogLog中存在相同的元素,则只保留一个。

假设您有两个HyperLogLog:
key1 有 5 个不重复元素
key2 有 3 个不重复元素
要将其合并为一个HyperLogLog,您可以使用以下命令:

PFMERGE key1 key2

三、Redis的HyperLogLog实战:

1. UV与PV是什么:

  • UV:全称Unique Visitor,也叫独立访客量,是指通过互联网访问、浏览这个网页的自然人。1天内同一个用户多次访问该网站,只记录1次。
  • PV:全称Page View,也叫页面访问量或点击量,用户每访问网站的一个页面,记录1次PV,用户多次打开页面,则记录多次PV。往往用来衡量网站的流量。

通常来说UV会比PV大很多,所以衡量同一个网站的访问量,我们需要综合考虑很多因素,所以我们只是单纯的把这两个值作为一个参考值

UV统计在服务端做会比较麻烦,因为要判断该用户是否已经统计过了,需要将统计过的用户信息保存。但是如果每个访问的用户都保存到Redis中,数据量会非常恐怖,那怎么处理呢?

Hyperloglog(HLL)是从Loglog算法派生的概率算法,用于确定非常大的集合的基数,而不需要存储其所有值。相关算法原理大家可以参考:https://juejin.cn/post/6844903785744056333#heading-0

Redis中的HLL是基于string结构实现的,单个HLL的内存永远小于16kb内存占用低的令人发指!作为代价,其测量结果是概率性的,有小于0.81%的误差。不过对于UV统计来说,这完全可以忽略。

2. UV统计-测试百万数据的统计

测试思路:我们直接利用单元测试,向HyperLogLog中添加100万条数据,看看内存占用和统计效果如何

@Test
    public void testHyperLogLog(){
    
    
        String[] users = new String[1000];
        int index = 0;
        for (int i = 1; i <= 100000; i++) {
    
    
            users[index++ ] = "user_" + i;
            // 每1000条发送一次
            if (i % 1000 == 0){
    
    
                index = 0;
                stringRedisTemplate.opsForHyperLogLog().add("hl1", users);
            }
        }
        // 统计数量
        Long size = stringRedisTemplate.opsForHyperLogLog().size("hl1");
        System.out.println("size==" + size);
    }

结果:

size==99839

从结果来分析,数据有一定的偏差,但相当的少,误差在0.00161

四、源码下载
https://gitee.com/charlinchenlin/koo-erp/

猜你喜欢

转载自blog.csdn.net/lovoo/article/details/131037609