美文网首页
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