1. 介绍
Redis 在 2.8.9 版本添加了 HyperLogLog 结构。
Redis HyperLogLog 是用来做基数统计的算法
HyperLogLog 的优点是,在输入元素的数量或者体积非常非常大时,计算基数所需的空间总是固定 的、并且是很小的
HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素本身。HyperLogLog的本质还是字符串
在开始这一节之前,我们先思考一个常见的业务问题:如果你负责开发维护一个大型的网站,有一天老板找产品经理要网站每个网页每天的 UV 数据,然后让你来开发这个统计模块,你会如何实现?
如果统计 PV 那非常好办,给每个网页一个独立的 Redis 计数器就可以了,这个计数器的 key 后缀加上当天的日期。这样来一个请求,incrby 一次,最终就可以统计出所有的 PV 数据。
但是 UV 不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的 ID,无论是登陆用户还是未登陆用户都需要一个唯一 ID 来标识。
你也许已经想到了一个简单的方案,那就是为每一个页面一个独立的 set 集合来存储所有当天访问过此页面的用户 ID。当一个请求过来时,我们使用 sadd 将用户 ID 塞进去就可以了。通过 scard 可以取出这个集合的大小,这个数字就是这个页面的 UV 数据。没错,这是一个非常简单的方案。
但是,如果你的页面访问量非常大,比如一个爆款页面几千万的 UV,你需要一个很大的 set 集合来统计,这就非常浪费空间。如果这样的页面很多,那所需要的存储空间是惊人的。为这样一个去重功能就耗费这样多的存储空间,值得么?其实老板需要的数据又不需要太精确,105w 和 106w 这两个数字对于老板们来说并没有多大区别,So,有没有更好的解决方案呢?
这就是本节要引入的一个解决方案,Redis 提供了 HyperLogLog 数据结构就是用来解决这种统计问题的。HyperLogLog 提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是 0.81%,这样的精确度已经可以满足上面的 UV 统计需求了。
HyperLogLog 数据结构是 Redis 的高级数据结构,它非常有用,但是令人感到意外的是,使用过它的人非常少。
2. 命令
HyperLogLog 提供了三个命令 pfadd,pfcount,pfmerge
2.1 pfadd
语法:pfadd key element [element……]
功能:
1. 将任意数量的元素添加到指定的HyperLogLog里面。
2. 作为这个命令的副作用,HyperLogLog内部可能会被更新,以便反映一个不同的唯一元素估计数量(也即是集合的基数)。
返回值:
整数回复:如果HyperLogLog的内部储存被修改了,那么返回1,否则返回0.
# 举例
pfadd 2019-12-06:unique:ids "uuid-1" "uuid-2" "uuid-3" "uuid-4"
# 结果(integer) 1
2.2 pfcount
语法:pfcount key [key……]
功能:
1. 当pfcount命令作用于当个键时,返回储存在给定键的HyperLogLog的近似基数,如果键不存在,那么返回0。
2. 当pfcount命令作用于多个键时,返回所有给定HyperLogLog的并集的近似基数,这个近似基数是通过将所有给定HyperLogLog合并至一个临时HyperLogLog来计算得出的。
# 举例
pfcount 2019-12-06:unique:ids
# 结果(integer) 4 因为在上边添加了4个用户
2.3 pfmerge
语法:pfmerge destkey sourcekey [sourcekey……]
功能:
将多个HyperLogLog合并(merge)为一个HyperLogLog,合并后的HyperLogLog的基数接近于所有输入HyperLogLog的可见集合(observed set)的并集。
# 举例
pfmerge 2019-12-06-07:unique:ids 2019-12-06:unique:ids 2019-12-07:unique:ids
# 返回值返回OK
3. 使用经验
- HyperLogLog有错误率,数据量大时不是百分百正确,但是错误率极低
- 取不出存入的数据,只能用于数据统计
所以使用HyperLogLog的时候是否容忍错误率,是否能容忍取不出存入的数据