「Redis」基数统计:HyperLogLog

不积跬步,无以至千里;不积小流,无以成江海。

码字不易,点赞再看。

场景分析

某天产品经理要我们统计网站每个页面每天的 UV 数据?接到这个需求你该怎么设计这个统计模块

考虑到 UV 的特性 同一个用户一天之内的多次访问请求只能计数一次。所以请求到网页的每一个用户

都需要带有唯一 ID 标识。

方案一

数据量比较低的情况下,通常是简单的把所有数据存储下来,直接count一下就是基数了

但随着数据量的急剧增大,这种方式已经很难满足需求。过大的数据量无论是在存储还是在查询方面都

存在巨大的挑战,都没法达到大数据时代的要求或者是性价比太低。

方案二用 set 记录 当天访问过此页面的用户 ID,当一个请求过来时,我们使用 sadd 将用户 ID 塞进去。通过 scard 可以取出这个集合的大小,这个数字就是这个页面的 UV 数据。

但是,如果你的页面访问量非常大,比如一个爆款页面几千万的 UV,就需要一个很大的 set 集合来统计,这就非常 浪费空间。如果这样的页面很多,那所需要的存储空间是惊人的。

为这样一个去重功能就耗费这样多的存储空间,值得么?这个数据真的需要非常精确吗?1000w 和 1001w 对产品经理来说差别不是很大。。


「Redis」基数统计:HyperLogLog

So 推出今天的主咖 HyperLogLog

HyperLogLog 是什么

HyperLogLog 与 布隆过滤器 都是针对 大数据 统计 存储 应用场景下的 知名算法。

HyperLogLog 是在大数据的情况下关于 数据基数 的空间复杂度优化实现。可以使用 12KB 内存,就可以计算接近 2^64 个不同元素的 基数

HyperLogLog 提供 不精确 的 去重计数 方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,误差可以通过设置的 辅助计算因子 进行降低,这样的精确度已经可以满足上面的 UV 统计需求了。

基数

基数就是指一个集合中不同值的数目,比如[a,b,c,d]的基数就是4,[a,b,c,d,a]的基数还是4,因为a重复了一个,不算。基数也可以称之为Distinct Value,简称DV。

HyperLogLog原理

给定一个集合 S,对集合中的每一个元素,我们做一个 哈希,假设生成一个 16 位的比特串,从所有生成的比特串中挑选出 前面 连续 0 次数(K) 最多 的比特串,假设为 0000000011010110,连续 0 的次数为 8,因此我们可以估计该集合 S 的基数介于 2^K 和 2^(K+1) 之间,用这种方式估计的值都等于 2^(K+1),这明显是不合理的。

因此在实际的 HyperLogLog 算法中,采取 分桶 平均原理了来消除误差。

特点:牺牲了一定的准确度(在一些场景下是可以忽略的),但却实现了空间复杂度上的极大的压缩,可以说是性价比很高的。

2^(K+1)为什么要加1呢?


这是一个数学细节,具体要看论文


简单的理解:就是为了进行概率估计

Redis 中 HyperLogLog 用法


「Redis」基数统计:HyperLogLog


「Redis」基数统计:HyperLogLog


注意事项

HyperLogLog 它需要占据 12k 的存储空间,所以它 不适合 统计 单个 用户相关的数据。如果你的用户上亿,可以算算,这个空间成本是非常大的。但是相比 set 存储方案,HyperLogLog 所使用的空间小巫见大巫了。

不过你也不必过于担心,因为 Redis 对 HyperLogLog 的存储进行了 优化,在计数比较小时,它的存储空间采用 稀疏矩阵 存储,空间占用很小,仅仅在计数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成 稠密矩阵,才会占用 12k 的空间。

点关注 不迷路

以上就是这篇的全部内容,能看到这里的都是 人才。

如果你从本篇内容有收获,求 点赞,求 关注,求 转发 ,让更多的人学习到。

如果本文有任何错误,请批评指教,不胜感激 !


分享到:


相關文章: