Redis - HyperLogLog
https://baijiahao.baidu.com/s?id=1669618381679082000&wfr=spider&for=pc
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。 Redis HyperLogLog 是用来做基数统计的算法,HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的。 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。 但是,因为 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身,所以 HyperLogLog 不能像集合那样,返回输入的各个元素。
什么是基数? 比如数据集 {1, 3, 5, 7, 5, 7, 8}, 那么这个数据集的基数集为 {1, 3, 5 ,7, 8}, 基数(不重复元素)为5。 基数估计就是在误差可接受的范围内,快速计算基数。
实例
以下实例演示了 HyperLogLog 的工作过程:
redis 127.0.0.1:6379> PFADD w3ckey "redis"
1) (integer) 1
redis 127.0.0.1:6379> PFADD w3ckey "mongodb"
1) (integer) 1
redis 127.0.0.1:6379> PFADD w3ckey "mysql"
1) (integer) 1
redis 127.0.0.1:6379> PFCOUNT w3ckey
(integer) 3
命令
| 命令 | 描述 |
|---|---|
| PFADD key element [element ...] | 添加指定元素到 HyperLogLog 中 |
| PFCOUNT key [key ...] | 添加指定元素到 HyperLogLog 中 |
| PFMERGE destkey sourcekey [sourcekey ...] | 将多个 HyperLogLog 合并为一个 HyperLogLog |
场景
1、
假如我要统计网页的UV(浏览用户数量,一天内同一个用户多次访问只能算一次),传统的解决方案是使用Set来保存用户id,然后统计Set中的元素数量来获取页面UV。但这种方案只能承载少量用户,一旦用户数量大起来就需要消耗大量的空间来存储用户id。我的目的是统计用户数量而不是保存用户,这简直是个吃力不讨好的方案!
而使用Redis的HyperLogLog最多需要12k就可以统计大量的用户 数,尽管它大概有0.81%的错误率,但对于统计UV这种不需要很精确的数据是可以忽略不计的。
HyperLogLog内存占用虽然小,但是并不准确,而且只能计算独立总数。
Redis提供的HyperLogLog数据类型的特征:
- 基本特征:使用HyperLogLog Counting(HLL)实现,只做基数计算,不会保存元数据。
- 内存占用:HyperLogLog每个KEY最多占用12K的内存空间,可以计算接近2^64个不同元素的基数,它的存储空间采用稀疏矩阵存储,空间占用很小,仅仅在计数基数个数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成稠密矩阵,转变成稠密矩阵之后才会占用12K的内存空间。
- 计数误差范围:基数计数的结果是一个标准误差(Standard Error)为0.81%的近似值,当数据量不大的时候,得到的结果也可能是一个准确值
内存占用小(每个KEY最高占用12K)是HyperLogLog的最大优势,而它存在两个相对明显的限制:
- 计算结果并不是准确值,存在标准误差,这是由于它本质上是用概率算法导致的。
- 不保存基数的元数据,这一点对需要使用元数据进行数据分析的场景并不友好。