Redis系列(四)--HyperLogLong入门实战和详解

1、使用场景redis

使用场景:算法

    统计网页访问量。spring

思考:怎么样统计网页访问量,而且一个IP一天访问屡次同一个页面,只能算一次?数据结构

分析:1.首先分析该统计数,是否须要正确,其实产品只须要一个大概的,一天100W,和一天110W,其实差很少。若是使用Java的话,那个list能够去重,同时在内存等相关上要占很小的比率。函数

解决方式:工具

方式一:传统方式,集合实现。优化

使用集合(set)来储存每一个访客的 IP ,经过集合性质(集合中的每一个元素都各不相同)来获得多个独立 IP .可是缺点也很大,假如访问量很大,你须要一个很大的 set 集合来统计,这就很是浪费空间。若是这样的页面不少,那所须要的存储空间是惊人的。为这样一个去重功能就耗费这样多的存储空间,值得么?spa

        使用字符串来储存每一个 IPv4 地址最多须要耗费 15 字节(格式为 'XXX.XXX.XXX.XXX' ,好比'192.168.10.127')。code

下表给出了使用集合记录不一样数量的独立 IP 时,须要耗费的内存数量:ip

独立 IP 数量一天一个月一年

一百万15 MB 450 MB 5.4 GB

一千万150 MB 4.5 GB 54 GB

一亿1.5 GB 45 GB 540 GB

随着集合记录的 IP 愈来愈多,消耗的内存也会愈来愈多。

另外若是要储存 IPv6 地址的话,须要的内存还会更多一些

 

方式二:redis 2.8.6 版本以后,新的命令功能,HyperLogLong。

Redis 提供了 HyperLogLog 数据结构就是用来解决这种统计问题的。HyperLogLog 提供不精确的去重计数方案,虽然不精确可是也不是很是不精确,标准偏差是 0.81%,这样的精确度已经能够知足上面的 UV 统计需求了。

2、概念和实战

        Redis HyperLogLog 是用来作基数统计的算法,HyperLogLog 的优势是,在输入元素的数量或者体积很是很是大时,计算基数所需的空间老是固定 的、而且是很小的。

在 Redis 里面,每一个 HyperLogLog 键只须要花费 12 KB 内存(由于 Redis 对 HyperLogLog 的存储进行了优化,在计数比较小时,它的存储空间采用稀疏矩阵存储,空间占用很小,仅仅在计数慢慢变大,稀疏矩阵占用空间渐渐超过了阈值时才会一次性转变成稠密矩阵,才会占用 12k 的空间。),就能够计算接近 2^64 个不一样元素的基 数。这和计算基数时,元素越多耗费内存就越多的集合造成鲜明对比。

可是,由于 HyperLogLog 只会根据输入元素来计算基数,而不会储存输入元素自己,因此 HyperLogLog 不能像集合那样,返回输入的各个元素。

        1.使用命令模式

pfadd test:aaron:ip "191.168.1.23"pfadd test:aaron:ip "191.168.1.24"#结果为2pfcount test:aaron:ip

        2.py代码





import redis#redis 链接pool = redis.ConnectionPool(host='127.0.0.1', port=6379)r = redis.Redis(connection_pool=pool)#HyperLogLogdef her_log():for i in range(100000):r.pfadd("test:log:aaron", "user%d" % i)print (100000, r.pfcount("test:log:aaron"))#主函数,执行行数if __name__ == '__main__':her_log()

        3.Java代码






package com.example.redis.zfr.demoredis.bit;import org.springframework.beans.factory.annotation.Autowired;import org.springframework.data.redis.connection.RedisStringCommands;import org.springframework.data.redis.core.RedisCallback;import org.springframework.data.redis.core.RedisTemplate;import org.springframework.stereotype.Component;/*** @author 繁荣Aaron* redis工具类*/@Componentpublic class RedisUtil {@Autowiredprivate RedisTemplate<Object, Object> redisTemplate;/*** 设置 HyperLogLong 的 key 值* @param key* @param value*/public Long pfadd(String key, String value){return redisTemplate.execute((RedisCallback<Long>) con -> con.pfAdd(key.getBytes(),value.getBytes()));}/*** 获取HyperLogLong 数量* @param key* @return*/public Long pfCount(String key){return redisTemplate.execute((RedisCallback<Long>) con -> con.pfCount(key.getBytes()));    }}

3、思考

1.pf 的内存占用为何是 12k?

解决:Redis 的 HyperLogLog 实现中用到的是 16384 个桶,也就是 2^14,每一个桶的 maxbits 须要 6 个 bits 来存储,最大能够表示 maxbits=63,因而总共占用内存就是2^14 * 6 / 8 = 12k字节。

2.两个页面合并访问量怎么作?

    可使用pfmerge命令。

    命令:

pfadd test:aaron:ip "191.168.1.23"pfadd test:aaron:ip "191.168.1.24"pfadd test:aaron:ip:merge "191.168.1.24"pfadd test:aaron:ip:merge "191.168.1.23"PFMERGE test:aaron:ip:merge:result test:aaron:ip test:aaron:ip:merge#结果为2PFCOUNT test:aaron:ip:merge:result

4、总结

HyperLogLog 实现独立 IP 计算功能:

独立 IP 数量一天一个月一年一年(使用集合)

一百万12 KB 360 KB 4.32 MB 5.4 GB

一千万12 KB 360 KB 4.32 MB 54 GB

一亿12 KB 360 KB 4.32 MB 540 GB

下表列出了使用 HyperLogLog 记录不一样数量的独立 IP 时,须要耗费的内存数量:

能够看到,要统计相同数量的独立 IP ,HyperLogLog 所需的内存要比集合少得多。

相关文章
相关标签/搜索