Redis HyperLogLog 解决统计问题

Redis 在 2.8.9 版本添加了 HyperLogLog 结构。

HyperLogLog 是最早由 Flajolet 及其同事在 2007 年提出的一种 估算基数的近似最优算法,用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。

在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。

但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。

什么是基数?

基数(cardinal number)在数学上,是集合论中刻画任意集合大小的一个概念。两个能够建立元素间一一对应的集合称为互相对等集合。例如3个人的集合和3匹马的集合可以建立一一对应,是两个对等的集合。(来源于百度百科)

举个列子:比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。

基数统计的常用方法

1)Set

熟悉Redis数据结构的同学一定首先会想到Set这个结构,我们只需要把数据都存入Set,然后用scard命令就可以得到结果,这是一种思路,但是存在一定的问题。如果数据量非常大,那么将会耗费很大的内存空间,如果这些数据仅仅是用来统计基数,那么无疑是造成了巨大的浪费,因此,我们需要找到一种占用内存较小的方法。

2)bitmap

bitmap 可以理解为通过一个 bit 数组来存储特定数据的一种数据结构,每一个 bit 位都能独立包含信息,bit 是数据的最小存储单位,因此能大量节省空间,也可以将整个 bit 数据一次性 load 到内存计算。如果定义一个很大的 bit 数组,基础统计中 每一个元素对应到 bit 数组中的一位,bitmap 还有一个明显的优势是 可以轻松合并多个统计结果,只需要对多个结果求异或就可以了,也可以大大减少存储内存。可以简单做一个计算,如果要统计 1 亿 个数据的基数值,大约需要的内存100_000_000/ 8/ 1024/ 1024 ≈ 12 M,如果用 32 bit 的 int 代表 每一个 统计的数据,大约需要内存32 * 100_000_000/ 8/ 1024/ 1024 ≈ 381 M

可以看到 bitmap 对于内存的节省显而易见,但仍然不够。统计一个对象的基数值就需要 12 M,如果统计 1 万个对象,就需要接近 120 G,对于大数据的场景仍然不适用。

在这种情况下,HyperLogLog将会出来拯救我们

HyperLogLog原理

HyperLogLog算法时一种非常巧妙的近似统计大量去重元素数量的算法,它内部维护了16384个桶来记录各自桶的元素数量,当一个元素过来,它会散列到其中一个桶。当元素到来时,通过 hash 算法将这个元素分派到其中的一个小集合存储,同样的元素总是会散列到同样的小集合。这样总的计数就是所有小集合大小的总和。使用这种方式精确计数除了可以增加元素外,还可以减少元素。

一个HyperLogLog实际占用的空间大约是 13684 * 6bit / 8 = 12k 字节。但是在计数比较小的时候,大多数桶的计数值都是零。如果 12k 字节里面太多的字节都是零,那么这个空间是可以适当节约一下的。Redis 在计数值比较小的情况下采用了稀疏存储,稀疏存储的空间占用远远小于 12k 字节。相对于稀疏存储的就是密集存储,密集存储会恒定占用 12k 字节。

大白话:例如 抛硬币的游戏:你连续掷 n 次硬币,然后说出其中连续掷为正面的最大次数,我来猜你一共抛了多少次

为什么需要HyperLogLog

如果要统计1亿个数据的基数值,大约需要内部才能100000000/8/1024/1024 约等于12M,内存减少占用的效果显著;
然而统计一个对象的基数值需要12M,如果统计10000个对象,就需要将近120G,同样不能广泛用于大数据场景;

HyperLogLog 的使用

HyperLogLog 提供了两个指令 PFADD 和 PFCOUNT,字面意思就是一个是增加,另一个是获取计数。PFADD 和 set 集合的 SADD 的用法是一样的,来一个用户 ID,就将用户 ID 塞进去就是,PFCOUNT 和 SCARD 的用法是一致的,直接获取计数值:

PFADD key element [element ...]:添加指定元素到HyperLogLog中
PFCOUNT key [key ...]:返回给定HyperLogLog的基数估算值
PFMERGE destkey sourcekey [sourcekey ...]:将多个HyperLogLog合并为一个HyperLogLog

应用场景

供不精确的去重计数功能,比较适合用来做大规模数据的去重统计,例如统计 UV;

如:统计注册 IP 数
统计每日访问 IP 数
统计页面实时 UV 数
统计在线用户数
统计每天搜索不同词条的个数
统计真实文章阅读数
等等

有一个神奇的网站,可以动态地让你观察到 HyperLogLog 的算法到底是怎么执行的::http://content.research.neustar.biz/blog/hll.html

其中的一些概念这里稍微解释一下,您就可以自行去点击 step 来观察了:

  • m 表示分桶个数

  • 蓝色的 bit 表示在桶中的位置

  • 绿色的 bit 表示第一个 1 出现的位置

  • 红色 bit 表示绿色 bit 的值的累加

猜你喜欢

转载自blog.csdn.net/liuxl57805678/article/details/108870958