Redis使用Set BitMap HyperLoglog统计日活,UV,IP地址

需求

平常都会有个需求统计:计算日活、7日活、月活数据 记录IP

SET方式

   @Autowired
    private RedisCacheService redisCacheService;


    private final static String key = "USER_COUNT";

    /**
     * 假如用户有100万,set存储100万个id号,如果一个id号占32个字节,
     * 总共就是差不多32M,一个月就是960M 差不多一个G了
     * 数据量上亿的场景下,例如几亿用户系统的签到,去重登录次数统计,
     * 某用户是否在线状态 : setbit 非常的牛逼的功能
     */
    @Test
    public void user_sadd() {

        String midKey = new DateTime().toString(Constant.DATE_C_YMD);

        redisCacheService.delete(key);

        for (int k = 0; k < 10; k++) {
            redisCacheService.sAdd(key, midKey + k);
        }

        long ksize = redisCacheService.sSize(key);

        System.out.println(" size  :  " + ksize);

        //result : 10
    }

BitMap

  /**
     *     100万个id号需要100万个bit位,也就是100万/8 = 125K字节  存储100万个用户,
     *     只需要125K个字节,一个月才4M
     */
    @Test
    public void test_sbit() {

        redisCacheService.delete(key);

        //====================登录========================

        for (int k = 0; k < 10; k++) {
            redisCacheService.setBit(key, k, Boolean.TRUE);
        }

        long ksize = redisCacheService.bitCount(key);

        System.out.println("No1 . size  :  " + ksize);


        List<Integer> list = Lists.newArrayList(1, 5, 7);

        list.forEach(x->{
            Boolean num = redisCacheService.getBit(key, x);
            System.out.println(" id  :  " + x + ((num) ? "在线" : "不在线"));
        });


        //====================操作后========================

        for (int k = 6; k < 9; k++) {
            redisCacheService.setBit(key, k, Boolean.FALSE);
        }

        ksize = redisCacheService.bitCount(key);

        System.out.println("No2 . size  :  " + ksize);


        list = Lists.newArrayList(1, 5, 7);

        list.forEach(x->{
            Boolean num = redisCacheService.getBit(key, x);
            System.out.println(" id  :  " + x + ((num) ? "在线" : "不在线"));
        });

//        No1 . size  :  10
//        id  :  1在线
//        id  :  5在线
//        id  :  7在线
//        No2 . size  :  7
//        id  :  1在线
//        id  :  5在线
//        id  :  7不在线
    }

BitCount代码

   /**
     * 统计bit位为1的总数
     * @param key
     */
    @Override
    public long bitCount(final String key) {
        return redisTemplate.execute(new RedisCallback<Long>() {
            @Override
            public Long doInRedis(RedisConnection connection) throws DataAccessException {
                long result = 0;
                result = connection.bitCount(key.getBytes());
                return result;
            }
        });
    }

HyperLoglog

  /**
     * 存储100万个独立用户只需要15K左右,一个月才480K左右
     *
     * 鉴于 HyperLogLog 不保存数据内容的特性,所以,它只适用于一些特定的场景。
     * 我这里给出一个最常遇到的场景需要:计算日活、7日活、月活数据 记录Ip
     *
     * 在 Redis 中每个键占用的内容都是 12K,理论存储近似接近 2^64 个值,
     * 不管存储的内容是什么。这是一个基于基数估计的算法,只能比较准确的估算出基数,
     * 可以使用少量固定的内存去存储并识别集合中的唯一元素。但是这个估算的基数并不一定准确,
     * 是一个带有 0.81% 标准错误(standard error)的近似值
     *
     * 在 Redis 里面,每个 HyperLogLog 键只需要花费 12 KB 内存,就可以计算接近 2^64 个不同元素的基 数。
     * 这和计算基数时,元素越多耗费内存就越多的集合形成鲜明对比。
     */
    @Test
    public void test_HyperLog() {

        String midKey = new DateTime().toString(Constant.DATE_C_YMD);

        redisCacheService.delete(key);

        for (int k = 0; k < 10; k++) {
            redisCacheService.pfAdd(key, midKey + k);
        }

        long ksize = redisCacheService.pfSize(key);

        System.out.println(" size  :  " + ksize);

        //  size  :  10
    }
HyperLoglog相关工具类
@Override
    public Long pfAdd(String key, String... values) {
        HyperLogLogOperations operations = redisTemplate.opsForHyperLogLog();
        return operations.add(key, values);

    }

    @Override
    public Long pfSize(String key) {
        HyperLogLogOperations operations = redisTemplate.opsForHyperLogLog();
        return operations.size(key);
    }

参考

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值