欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  php教程

Redis基数统计HyperLogLog小内存大用处

程序员文章站 2022-03-08 20:43:46
...
我们一直都知道,redis几大常用数据结构,字符串、散列、列表、集合、有序集合。其实后来Redis做了很多补充,其中之一就是HyperLogLog,另外的还有GEO(地理位置),是3.2版本加的。

这里我们就来简单介绍下HyperLogLog结构。

先说用处:这个结构可以非常省内存的去统计各种计数,比如注册ip数、每日访问IP数、页面实时UV(PV肯定字符串就搞定了)、在线用户数等。

这里看到所有的用处都是xxx数,所以这个数据结构的特点就是,可以比较准确的估算出你要统计的数量,但是却无法知道统计的详细内容。比如统计每日访问IP数,可以获取当时访问过的IP总数量,但是没法知道这些IP都是什么。

有得必有失,当然你要统计上面提到的那些内容,可以用集合来处理,这样可以知道数量,也能获得所有的详细列表。但是一个大型的网站,每天IP比如有100万个呢,我们粗算一个IP消耗15字节,那么100万个IP就是15M,如果1千万,就是150M。

再来看看我们的HyperLogLog,在Redis中每个键占用的内容都是12K,理论存储近似接近2^64个值,不管存储的内容是什么。12K,知道这个数据结构的作用了吧。这也是为什么他不能知道里面的详细内容了。这是一个基于基数估算的算法,只能比较准确的估算出基数,可以使用少量固定的内存去存储并识别集合中的唯一元素。而且这个估算的基数并不一定准确,是一个带有 0.81% 标准错误(standard error)的近似值。

这里当你记录的内容越多,和集合使用的内容就越容易产生鲜明的对比,因为HyperLogLog结构,在范围允许的情况下无论多少值,都置灰占用12K内存。

这样比如我们把每日IP记录下来,假设每天有一亿个IP访问,如果使用集合的话,一天的内存使用就是1.5G,假设我们存储一个月的记录,就需要45G容量。但是使用HyperLogLog的话,一天12K,一个月360K。如果我们不需要知道IP具体信息的话,完全可以把这些记录留在内存一年、或者不删都行。如果需要,我们也会把所有的IP访问记录通过其他途径存储起来。把每天的信息存储起来,我们可以计算每月IP总数(MERGE),一年的IP总数等(去重)。

下面介绍一下HyperLogLog的命令,其实他和集合的命令比较像,只是命令少,不能获取列表而已。另外这个数据结构需要2.8.9及以上的版本才能使用哦~

PFADD

在执行这个命令之后,HyperLogLog内部的结构会被更新,并有所反馈,如果执行完之后HyperLogLog内部的基数估算发生了变化,那么就会返回1,否则(认为已经存在)就返回0。
这个命令还有一个比较神器的就是可以只有键,没有值,这样的意思就是只是创建空的键,不放值。
如果这个键存在,不做任何事情,返回0;不存在的话就创建,并返回1。

这个命令的时间复杂度为O(1),所以就放心用吧~

命令例子:

redis> PFADD  ip:20160929  "1.1.1.1"  "2.2.2.2"  "3.3.3.3"
(integer) 1
redis> PFADD  ip:20160929 "2.2.2.2"  "4.4.4.4"  "5.5.5.5"  # 存在就只加新的
(integer) 1
redis> PFCOUNT  ip:20160929  # 元素估计数量没有变化
(integer) 5
redis> PFADD  ip:20160929 "2.2.2.2"  # 存在就不会增加
(integer) 0

其实我们发现在少的时候还是挺准的,哈哈。

PFCOUNT

其实在上面的学习中我们已经用过这个了,这里再来介绍下。

当命令作用于单个键的时候,返回这个键的基数估算值。如果键不存在,则返回0。
当作用于多个键的时候,返回这些键的并集估算值。类似于把这些键都合并了之后,在调用这个命令输出。

这个命令在作用于单个值的时候,时间复杂度为O(1),并且具有非常低的平均常数时间;在作用于N个值的时候,时间复杂度为O(N),这个命令的常数复杂度会比较低些。

命令例子:

redis> PFADD  ip:20160929  "1.1.1.1"  "2.2.2.2"  "3.3.3.3"
(integer) 1
redis> PFCOUNT  ip:20160929
(integer) 3
redis> PFADD  ip:20160928  "1.1.1.1"  "4.4.4.4"  "5.5.5.5"
(integer) 1
redis> PFCOUNT  ip:20160928  ip:20160929
(integer) 5

PFMERGE

合并(merge)多个HyperLogLog为一个HyperLogLog。其实这个也很好理解,而合并后的估算基数也近似于所有HyperLogLog估算基数的并集。

这个命令的第一个参数为目标键,剩下的参数为要合并的HyperLogLog。命令执行时,如果目标键不存在,则创建后再执行合并。

这个命令的时间复杂度为O(N),其中N为要合并的HyperLogLog的个数。不过这个命令的常数时间复杂度比较高。

命令例子:

edis> PFADD  ip:20160929  "1.1.1.1"  "2.2.2.2"  "3.3.3.3"
(integer) 1
redis> PFADD  ip:20160928  "1.1.1.1"  "4.4.4.4"  "5.5.5.5"
(integer) 1
redis> PFMERGE ip:201609   ip:20160928   ip:20160929
OK
redis> PFCOUNT  ip:201609
(integer) 5

到此HyperLogLog所有的命令就都介绍完了,没错,目前就只有这三个。其实也很简单的,知道了这个结构的用法,也就知道什么时候适合用了,对我们非常珍贵的内存还是很有帮助。