点击上方 IT牧场 ,选择 置顶或者星标
技术干货每日送达
redis四两拨千斤-HyperLogLog
UV统计场景
假设我们有这样的一个场景,需要我们统计一个页面的每天有多少用户访问(UV)。通常情况下我们肯定会想到使用Set集合来存储UserId,Set集合会帮我们把UserId去重,我们直接获取Set的长度就可以了。
这种方法有一个很大的问题,就是所占用的空间非常的大。假如有100万个用户,我们只存储用户的int类型的UserId,100万 * 4byte / 1024Kb /1024Mb = 3.8G,将近4个G的数据,这还是一天的统计数据,我们就占用了4个G!这种方式绝不可取。那有没有一种既可以完成目前的需求,又可以占用很少空间的方法呢?
下面祭出Redis的杀手锏HyperLogLog,它是一种数据结构自带去重效果,在Redis中只占用12kb,却可以计算2^62个数(相当于10^14个用户)。值得注意的是,他是一估算值,存在一个小于1%的误差。通常我们统计一个页面的UV的时候不需要特别准确的数据,对于老板老来说105w和106w没什么区别。
基本用法
HyperLogLog只有三个命令使用起来非常简单
PFADD
语法:PFADD key val1 val2 val3
pfadd 命令有两个入参,key为redis的key,如果key不存在会自动创建一个,val为数据集的基数,HyperLogLog并不会直接将基数添加到数据集中,而是根据基数来进行估算之前是否遇到过当前的val,具体的估算逻辑还是有些复杂的。
127.0.0.1:6379> pfadd pfkey 1 2 3 3
(integer) 1
127.0.0.1:6379> pfcount pfkey
(integer) 3
PFCOUNT
语法:PFCOUNT key
pfcount 命令返回给定 HyperLogLog 的基数估算值。
127.0.0.1:6379> pfcount pfkey
(integer) 3
PFMERGE
语法:PFMERGE destkey sourcekey
命令将多个 HyperLogLog 合并为一个 HyperLogLog ,合并后的 HyperLogLog 的基数估算值是通过对所有 给定 HyperLogLog 进行并集计算得出的。
127.0.0.1:6379> pfadd pfkey 1 2 3 3
(integer) 1
127.0.0.1:6379> pfcount pfkey
(integer) 3
127.0.0.1:6379> pfadd pfkey2 1 2 3
(integer) 1
127.0.0.1:6379> pfcount pfkey2
(integer) 3
127.0.0.1:6379> pfmerge pfkey pfkey1
OK
127.0.0.1:6379> pfcount pfkey
(integer) 3
使用案例
简单的java实现
public static void main(String[] args) {
JedisPool jedisPool = new JedisPool(host,port);
Jedis jedis = jedisPool.getResource();
//清空 key
jedis.del("keypf");
//循环写入数据
for (int i = 0; i < 1000; i++) {
jedis.pfadd("keypf", UUID.randomUUID().toString());
}
//获取统计数据
long pfcount = jedis.pfcount("keypf");
System.out.println("1000次写入,count的结果="+pfcount);
}
干货分享
最近将个人学习笔记整理成册,使用PDF分享。关注我,回复如下代码,即可获得百度盘地址,无套路领取!
•001:《Java并发与高并发解决方案》学习笔记;•002:《深入JVM内核——原理、诊断与优化》学习笔记;•003:《Java面试宝典》•004:《Docker开源书》•005:《Kubernetes开源书》•006:《DDD速成(领域驱动设计速成)》•007:全部•008:加技术群讨论
近期热文
•LinkedBlockingQueue vs ConcurrentLinkedQueue•解读Java 8 中为并发而生的 ConcurrentHashMap•Redis性能监控指标汇总•最全的DevOps工具集合,再也不怕选型了!•微服务架构下,解决数据库跨库查询的一些思路•聊聊大厂面试官必问的 MySQL 锁机制
关注我
喜欢就点个"在看"呗^_^