原文:6.【Redis系列】Redis的高级应用-HyperLogLog
老规矩还是先假设一个场景:比如京东的商品详情页,如果需要你来统计每天的UV数据,你会如何实现?
如果是PV就好办了,直接给每个网页增加一个计时器,每个网页增加一个日期,这样一进来incrby一次,最终可以计算出每天的统计所有的PV数据。
但是UV就不一样了,每一个用户进来多次每天也只能算一个UV。无论是登录用户还是未登录用户,都需要给一个唯一的ID来标识。
有可能你已经想到了通过set集合去重的功能,为每一个页面创建一个set集合,每进来一次,都add一下,最后通过scard取出集合的大小就可以了,这是一个非常简单的方案,如果爆品页每天的UV几千万的话,这样是非常浪费空间资源的。如果这样的页面很多,存储空间是惊人的。为了去重浪费这么大空间是不值得的。
Redis提供了一种解决方案,使用Hyper log log数据结构来解决这种统计的问题,Hyper log log提供的是不精确的去重计数方案,虽然不精确但也不是完全不精确,标准误差在0.81%左右。Hyper logLog是Redis的高级数据结构,非常有用,但是使用的人很少。
使用方法
127.0.0.1:6379> pfadd codehole user1
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 1
127.0.0.1:6379> pfadd codehole user2
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 2
127.0.0.1:6379> pfadd codehole user3
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 3
127.0.0.1:6379> pfadd codehole user4
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 4
127.0.0.1:6379> pfadd codehole user5
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 5
127.0.0.1:6379> pfadd codehole user6
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 6
127.0.0.1:6379> pfadd codehole user7 user8 user9 user10
(integer) 1
127.0.0.1:6379> pfcount codehole
(integer) 10
上面说HyperLogLog不精确的统计方案,下面我们用java来测试一下:
public class JedisTest {
public static void main(String[] args) {
Jedis jedis = new Jedis();
for (int i = 0; i < 100000; i++) {
jedis.pfadd("codehole", "user" + i);
}
long total = jedis.pfcount("codehole");
System.out.printf("%d %d\n", 100000, total);
jedis.close();
}
}
跑了约半分钟,我们看输出:
> python pftest.py
100000 99723
差了 277 个,按百分比是 0.277%,对于上面的 UV 统计需求来说,误差率也不算高。然后我们把上面的脚本再跑一边,也就相当于将数据重复加入一边,查看输出,可以发现,pfcount 的结果没有任何改变,还是 99723,说明它确实具备去重功能。
pfmerge 适合什么场合用?
HyperLogLog 除了上面的 pfadd 和 pfcount 之外,还提供了第三个指令 pfmerge,用于将多个 pf 计数值累加在一起形成一个新的 pf 值。
比如在网站中我们有两个内容差不多的页面,运营说需要这两个页面的数据进行合并。其中页面的 UV 访问量也需要合并,那这个时候 pfmerge 就可以派上用场了。
注意事项
HyperLogLog 这个数据结构不是免费的,不是说使用这个数据结构要花钱,它需要占据一定 12k 的存储空间,所以它不适合统计单个用户相关的数据。如果你的用户上亿,可以算算,这个空间成本是非常惊人的。但是相比 set 存储方案,HyperLogLog 所使用的空间那真是可以使用千斤对比四两来形容了。
不过你也不必过于担心,因为 Redis 对 HyperLogLog 的存储进行了优化,在计数比较小时,它的存储空间采用稀疏矩阵存储,空间占用很小,仅仅在计数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成稠密矩阵,才会占用 12k 的空间。
原文地址:https://www.cnblogs.com/lonelyxmas/p/12515048.html