最近还是想好好打下基础,先看看redis,参考gitbook上面的《Redis开发运维实践指南》。看到HyperLogLog类型,有点懵逼,看了书里面推荐的文章,大概是一知半解了,mark一下。
简介
HyperLogLog是一种可以实现计数问题的方案或者说算法,redis实现了HyperLogLog,把HyperLogLog作为一种数据类型/结构。
解决问题:计数问题(计算集合的基数)
优点:占用空间小,1.5K可以对十亿级的数据进行计数(2%的误差)
缺点:基于hash的统计计数(我这么翻译的,英文叫probabilistic counters),有误差
计数问题
给定一个值空间S,有N个元素,给定一个行为,会决定一个/批元素是否进入一个该空间的子集Sub,我们需要计算出经过任意次行为后这个子集Sub中包含的元素个数C。
PS:我也不知道原始的问题是怎么定义,自己瞎比定义一下
计数方案
方案一:使用hash表或者集合(set)
N个元素,需要k=ceil(log2N)位来表达,存满N个元素,需要kN位,量级是0(kN)。将问题具体一下,一个系统有1亿用户,每个用户的标识至少是27bit,就是4B。至少需要4B*227 = 229B≈500M。同理10亿用户约为4B*230=4G。考虑到大多数系统的用户id,都是留了很多冗余空间的,再加上,数据结构本身占用的空间,一个Set或者Hash表保存十亿用户id占用的空间往往是几十G。
优点:计数精确。
缺点:占用空间较大。数据量大时,几乎不可行。
方案二:使用bitmap
使用bitmap,需要一个合适的hash函数配合。可以大幅降低占用空间,但是十亿数据,大概也要百M(10亿/8B≈128M)级别的空间。
优化:使用稀疏bitmap,可以使占用的空间随C的大小变化。
优点:十亿数据占用空间在0M~100M间波动,随C越大,占用空间越大。
方便进行位操作
缺点:计数量大时还是占用了100M空间,而且依赖Hash算法,存在少量冲突。
方案三:使用统计计数
通过以上描述,计数问题的难点也差不多说明白了。这个问题在很多应用中都存在,比如点赞统计,比如点击(观看、访问)XX的用户数统计等等。
幸运的是,我们有统计计数方案,来作为以上方案的补充。我们回到一道小学数学题。
问:一个池塘里有很多鱼,随机抓100条,打上标记,放回去。再随机捞100条,有N条带标记的鱼,请问池塘里大概有多少条鱼。
解:100/x = n/100,x=100*100/n。
如果有一种隐射方式将N个数随机映射到M个数上(M<<N),我们随机把这C个数也隐射一下,数量记为MC,C≈N*MC/M。由于M远小于N,所以这个隐射肯定是无法做到1对1映射,所以肯定有误差。
小结
本质上,三种方案都使用了映射。方案一将一个数据映射成了log2Nbit,方案二将一条数据映射为了1bit,而方案三将多条数据映射为了1bit。亿级数据情况下,三种方案的比较如下。
Hash/Set | Bitmap | probabilistic counters |
|
空间 | G级(10G) | M级(100M) | K级(10K) |
误差 | 0 | 极低(可忽略) | 百分位 |
可行性 | 几乎不可行 | 可行 | 条件可行 |
条件可行意为,是否能接受百分位的误差,如果接受就可行,不接受就不可行。
统计计数probabilistic counters
上面提到统计计数方案,只是用了一种最简单的统计方法,是本人自行理解的一种方案。大神们肯定有更优化的方案,主要有线性统计计数(Linear Probabilistic Counter)、HyperLogLog。参考http://highscalability.com/blog/2012/4/5/big-data-counting-how-to-count-a-billion-distinct-objects-us.html。
当然了,如果有兴趣,可以翻一翻相关的文章,应该会有收获。