HyperLogLog
1.描述
HyperLogLog是一种概率数据结构,也被简称为HLL,用于估计集合的基数(总数)。和集合的用法基本一致,在使用时可以当做是在操作一个集合,但是HLL与集合的不同点在于HLL作为一种概率数据结构,以完美的精度换区了高效的空间利用率。这意味着HLL与集合在存储相同数据量的情况下会节省更多的空间,但同时精度不会完美,Redis官方给出的误差在0.81%左右。
Redis中的HLL虽然在技术上是一种不同的数据结构,但被编码为Redis字符串。
2.命令
(1)PFADD
PFADD key [element [element ...]]
添加一个或者多个元素到HLL中。
> pfadd hll a b c d e f g
(integer) 1
> pfcount hll
(integer) 7
(2)PFCOUNT
PFCOUNT key [key ...]
计算一个或者或多个HLL的元素总数和。
> pfadd hll1 a b c d
(integer) 1
> pfadd hll2 c d e f g
(integer) 1
> pfcount hll1 hll2
(integer) 7
(3)MERGE
PFMERGE destkey [sourcekey [sourcekey ...]]
合并多个HHL到一个新的HHL中,destkey是合并后新HLL的键。
> pfmerge all-hhl hll1 hll2
"OK"
> pfcount all-hhl
(integer) 7
3.误差验证
我们使用HHL和set分别存储1-100万,一共100万条数据来进行测试。
我们使用lua脚本新建一个100万条数据的hhl,使用pfcount命令可以看到估算出的总数是1009972。由此可以验证确实是有误差的,误差概率大约为1%。
> eval 'for i = 1, 1000000 do
redis.call("pfadd","hhl",i)
end' 0
(nil)
> pfcount hhl
(integer) 1009972
接下来我们再使用lua脚本新建一个100万条数据的set集合,可以看到计算出的总数是准确无误的,这是因为在Redis的源码中,set集合对象会有一个变量是专门用来存储集合中元素总数的,每当有新元素加入到集合中,都会使总数增加,所以是准确无误的。
> eval 'for i = 1, 1000000 do redis.call("sadd","set",i) end' 0
(nil)
> scard set
(integer) 1000000
4.内存占用比较
我们再来验证一下是不是如官方所说HHL的内存空间利用比率会比set集合高。在Redis中可以使用MEMORY USAGE key
命令来查看一个键占用的内存空间大小。
> memory usage hhl
(integer) 14384 #约等于14KB
> memory usage set
(integer) 40388752 #约等于39MB
我们可以看到在内存空间的利用上差距还是很大的,如果存储的别的信息还会更加明显。
5.使用案例
(1)某音乐平台统计每个专辑被收听的次数。
(2)抖音统计视频的浏览量,或者是点赞数。在抖音中我们看到如果点赞量很多的话基本上会显示13.6K,102W,并不会详细到个位数。
(3)一个购物网站有不同的分类,比如服装,视频,家电等。。。 可以统计每个分类页面的用户的唯一访问量。