redis专题05 海量数据处理之HyperLogLog
  热度 °
HyperLoglog
是redis
新支持的两种类型中的另外一种(上一种是位图类型Bitmaps
)。主要适用场景是海量数据的计算。特点是速度快, 占用空间小。
同样是用于计算,Bitmaps
可能更适合用于验证的大数据,比如签到,记录某用户是不是当天进行了签到,签到了多少天的时候。也就是说,你不光需要记录数据,还需要对数据进行验证的时候使用Bitmaps
。
而HyperLoglog
则用于只记录的时候,如统计每个网页每天的访问UV,
如果统计PV
那非常好办,给每个网页一个独立的Redis
计数器就可以了,这个计数器的key
后缀加上当天的日期。这样来一个请求,incrby
一次,最终就可以统计出所有的PV
数据。
但是UV
不一样,它要去重,同一个用户一天之内的多次访问请求只能计数一次。这就要求每一个网页请求都需要带上用户的ID
,无论是登陆用户还是未登陆用户都需要一个唯一ID
来标识。
你也许已经想到了一个简单的方案,那就是为每一个页面一个独立的set
集合来存储所有当天访问过此页面的用户ID
。当一个请求过来时,我们使用sadd
将用户ID
塞进去就可以了。通过scard
可以取出这个集合的大小,这个数字就是这个页面的UV
数据。没错,这是一个非常简单的方案。
但如果页面访问量非常大,比如一个爆款页面几千万的UV
,你需要一个很大的set
集合来统计,这就非常浪费空间。如果这样的页面很多,那所需要的存储空间是惊人的。为这样一个去重功能就耗费这样多的存储空间,值得么?
由此可以用redis
提供的HyperLogLog
数据结构来解决这种统计问题的。
HyperLogLog
提供不精确的去重计数方案,虽然不精确但是也不是非常不精确,标准误差是0.81%
,这样的精确度已经可以满足上面的UV
统计需求了。
HyperLogLog
数据结构基础应用
redis
为操作HyperLogLog
数据结构提供了三条命令,
pfadd
添加计数pfcount
获取计数pfmerge
合并两个数据的数据pfadd
添加数据
命令:
PFADD key element [element ...]
- 功能:将除了第一个参数以外的参数存储到以第一个参数为变量名的
HyperLogLog
结构中。
- 如果一个
HyperLogLog
的估计的近似基数在执行命令过程中发了变化,PFADD
返回1,否则返回0,如果指定的key不存在,这个命令会自动创建一个空的HyperLogLog
结构(指定长度和编码的字符串)。- 如果在调用该命令时仅提供变量名而不指定元素也是可以的,如果这个变量名存在,则不会有任何操作,如果不存在,则会创建一个数据结构。
- 返回值:如果
HyperLogLog
的内部被修改了,那么返回 1,否则返回 0 。
pfcount
命令:
PFCOUNT key [key ...]
- 功能:当参数为一个
key
时,返回存储在HyperLogLog
结构体的该变量的近似基数,如果该变量不存在,则返回0。
- 当参数为多个
key
时,返回这些HyperLogLog
并集的近似基数,这个值是将所给定的所有key
的HyperLoglog
结构合并到一个临时的HyperLogLog
结构中计算而得到的;HyperLogLog
可以使用固定且很少的内存(每个HyperLogLog
结构需要12K字节再加上key本身的几个字节)来存储集合的唯一元素。返回的可见集合基数并不是精确值, 而是一个带有 0.81% 标准错误(standard error
)的近似值。- 返回值:
PFADD
添加的唯一元素的近似数量;
pfadd
和pfcount
用法和set
集合的sadd
和scard
是一样的,添加数据,获取计数/长度;
1 | pfadd 添加计算,若修改了HyperLogLog结构则返回1 ,否则返回0 |
pfmerge
命令:
PFMERGE destkey sourcekey [sourcekey ...]
- 功能:将多个
HyperLogLog
合并(merge
)为一个HyperLogLog
, 合并后的HyperLogLog
的基数接近于所有输入HyperLogLog
的可见集合(observed set
)的并集;
- 合并得出的
HyperLogLog
会被储存在目标变量(第一个参数)里面, 如果该键并不存在, 那么命令在执行之前, 会先为该键创建一个空的;- 返回值:这个命令只会返回 OK
1 | 127.0.0.1:6379> pfadd home user1 |
HyperLogLog
实现原理
上述实现过程中可以看到HyperLogLog
在统计计数时可以很好的处理重复值问题, 那它是如何做到的呢? 这就需要进一步了解HyperLogLog
的内部实现原理了,
redis
内部HyperLogLog
中会先给其分配一定数量的桶, 这些桶就是用来存储创建的key
值的, 即pfadd key elmement
中的key
, 然后通过hash
将element
存储下来, 这样相同的element
的近似基数是一样的,所以相同的element
插入不在往桶中添加了,故在pfcount
基数时 则只会算一次了,
上说的pfcount
计算的是近似估值,误差在0.81%
标准错误, 这是因为HyperLogLog
在统计时计算的就是估值而不是精确值,因为在pfadd
时是通过element
的近似基数进行更新HyperLogLog
结构的, 案例分析如下,
`pfadd nums 随机数1, 随机数2, 随机数3 … 随机数N
如上, 给定一系列的随机整数,通过记录下低位连续零位的最大长度k
,通过这个k
值可以估算出随机数的数量,实验发现K
和N
的对数之间存在显著的线性相关性, 通过这种线性近似计算可以得到pfcount
指定key
的近似估值, 详细的原理这里不在进一步阐述;
可进一步参看HyperLogLog 复杂的公式推导
redis
的HyperLogLog
实现中用到的是16384
个桶,也就是2^14
,每个桶的maxbits
需要6 bits
来存储,最大可以表示maxbits=63
,于是总共占用内存就是2^14 * 6 / 8 = 12k
字节。
总结
- 从处理海量数据count distict问题场景入手,引入
redis
的HyperLogLog
数据结构解决方案,总结了使用方法并进行了实例分析说明; - 对
HyperLogLog
如何进行count distict
计数的原理进行了简要探讨和实例分析说明,并给出了进一步参看建议; HyperLogLog
结构主要是为了count-distinct
问题,尤其是处理海量数据时,速度快,占用内存小,但是统计值是有误差的,并且只能递增,不能递减;redis
对HyperLogLog
的存储进行了优化,在计数比较小时,它的存储空间采用稀疏矩阵存储
,空间占用很小,仅仅在计数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成稠密矩阵
,才会占用12k
的空间;
作者署名:朴实的一线攻城狮
本文标题:redis专题05 海量数据处理之HyperLogLog
本文出处:http://researchlab.github.io/2018/02/18/redis-05-hyperloglog/
版权声明:本文由Lee Hong创作和发表,采用署名(BY)-非商业性使用(NC)-相同方式共享(SA)国际许可协议进行许可,转载请注明作者及出处, 否则保留追究法律责任的权利。