美文网首页
hyperLoglog的使用

hyperLoglog的使用

作者: guessguess | 来源:发表于2020-10-28 11:03 被阅读0次

    首先来说一下业务场景吧。
    如果需要去统计某个行为,如请求的发起次数,用redis去模拟这个行为就是如下,使用一个key=request_1的键值对,来保存该请求的处理次数即可,自增模拟访问请求。如果针对多个请求的统计,使用多个键值对去保存即可。

    127.0.0.1:6379> set request_1 0
    OK
    127.0.0.1:6379> INCR request_1
    (integer) 1
    127.0.0.1:6379> INCR request_1
    (integer) 2
    127.0.0.1:6379> INCR request_1
    (integer) 3
    127.0.0.1:6379> INCR request_1
    (integer) 4
    127.0.0.1:6379> INCR request_1
    (integer) 5
    127.0.0.1:6379> INCR request_1
    (integer) 6
    

    那么问题来了,如果是对某个用户对某个请求的次数去重呢?就是某个用户访问某个请求,历史只算一次,我们按简单的来的话,java里面的数据结构必然会想到set,因为去重。那么redis里面也有set。简单概括一下就是,总共有多少个客户访问了某个请求,如果用set的话,可以实现功能,对于小项目来说,客户数量不多,数据量也不会很庞大。是可行的。
    但是对大项目来说呢?这个set只会无限扩张,最后变得很庞大,非常占用内存,变得很臃肿。
    那么如何既能够实现该功能,又可以减少内存的浪费呢,可以考虑使用hyperLoglog

    先来基本的使用一下(命令为什么是pf,因为作者的姓名简称就是Pf)
    模拟对requset_1的浏览用户的添加

    127.0.0.1:6379> PFADD request_1_users user1
    (integer) 1
    127.0.0.1:6379> PFADD request_1_users user2
    (integer) 1
    127.0.0.1:6379> PFADD request_1_users user3
    (integer) 1
    

    统计出requset_1的浏览用户数

    127.0.0.1:6379> PFCOUNT request_1_users
    (integer) 3
    

    如果某天,需要知道哪些客户,既访问了请求1又访问了请求2怎么办?
    这个时候还有一个很强大的功能PFMERGE,我们来模拟一下

    127.0.0.1:6379> PFADD request_1_users user1
    (integer) 1
    127.0.0.1:6379> PFADD request_1_users user2
    (integer) 1
    127.0.0.1:6379> PFADD request_1_users user3
    (integer) 1
    127.0.0.1:6379> PFADD request_2_users user1
    (integer) 1
    127.0.0.1:6379> PFADD request_2_users user2
    (integer) 1
    127.0.0.1:6379> PFADD request_2_users user4
    (integer) 1
    127.0.0.1:6379> PFCOUNT request_1_users
    (integer) 3
    127.0.0.1:6379> PFCOUNT request_2_users
    (integer) 3
    127.0.0.1:6379> PFMERGE request_1_2_users request_1_users request_2_users
    OK
    127.0.0.1:6379> PFCOUNT  request_1_2_users
    (integer) 4
    

    通过PFMERGE指令,可以将多个hyperLoglog合在一块,生成新的集合。request_1_users集合有user1,user2,user3 request_2_users有user1,user2,user4,所以最终去重合并后的集合数应该为4.

    概括一下就是hyperLoglog具备了以下功能
    1.pfadd 往集合添加成员
    2.pfcount 统计集合的长度
    3.pfmerge 将多个集合融合成一个新集合
    特点:数量特别大的时候内存占用只有12k,远远小于set占用的内存,另外数量大的时候可能会存在较小误差(即实际上pfadd了一亿次,而pfcount的数量少于1亿,但是这俩个的值区别不会太大,待会验证一下)

    那么我们通写代码的方式来使用一下
    首先看看redisTemplate对hyperLoglog的支持,总共有几个方法,add,size,delete,union,添加,统计,删除,融合,基本的都支持了。

    public interface HyperLogLogOperations<K, V> {
    
        /**
         * Adds the given {@literal values} to the {@literal key}.
         *
         * @param key must not be {@literal null}.
         * @param values must not be {@literal null}.
         * @return 1 of at least one of the values was added to the key; 0 otherwise. {@literal null} when used in pipeline /
         *         transaction.
         */
        Long add(K key, V... values);
    
        /**
         * Gets the current number of elements within the {@literal key}.
         *
         * @param keys must not be {@literal null} or {@literal empty}.
         * @return {@literal null} when used in pipeline / transaction.
         */
        Long size(K... keys);
    
        /**
         * Merges all values of given {@literal sourceKeys} into {@literal destination} key.
         *
         * @param destination key of HyperLogLog to move source keys into.
         * @param sourceKeys must not be {@literal null} or {@literal empty}.
         * @return {@literal null} when used in pipeline / transaction.
         */
        Long union(K destination, K... sourceKeys);
    
        /**
         * Removes the given {@literal key}.
         *
         * @param key must not be {@literal null}.
         */
        void delete(K key);
    
    }
    

    直接上代码

    @Component
    public class HyperLoglogUtils {
        
        @Autowired
        private RedisTemplate<String, String> jedisTemplate;
        
        public Long add(String key, String ... value) {
            return jedisTemplate.opsForHyperLogLog().add(key, value);
        }
        
        public void del(String key) {
            jedisTemplate.opsForHyperLogLog().delete(key);
        }
        
        public Long merge(String destination, String ...sourceKeys) {
            return jedisTemplate.opsForHyperLogLog().union(destination, sourceKeys);
        }
        
        public Long count(String key) {
            return jedisTemplate.opsForHyperLogLog().size(key);
        }
    }
    

    下面校验一下误差

    public class TestHyperLoglog extends TestApplication{
        
        @Autowired
        private HyperLoglogUtils hyperLoglogUtils;
        
        @Test
        public void add() {
            for(int i = 0; i < 100000; i ++) {
                hyperLoglogUtils.add("my_req_one", "user" + i);
            }
            System.out.println(hyperLoglogUtils.count("my_req_one"));
        } 
    }
    

    最终输出结果99725 误差还是可以接受的,千分之225

    相关文章

      网友评论

          本文标题:hyperLoglog的使用

          本文链接:https://www.haomeiwen.com/subject/mczomktx.html