Redis——用户签到BitMap,UV统计

目录

BitMap

使用场景

[1. 用户签到系统](#1. 用户签到系统)

[2. 用户行为标记](#2. 用户行为标记)

[3. 布隆过滤器(Bloom Filter)](#3. 布隆过滤器(Bloom Filter))

BitMap介绍

Redis中的使用

Redis功能示例

添加:

获取:

批量获取:

java中实现

统计本月连续签到次数

UV统计

[UV 统计的核心需求](#UV 统计的核心需求)

[使用 HyperLogLog](#使用 HyperLogLog)

[UV 统计的常见场景](#UV 统计的常见场景)

[场景 1:每日 UV 统计](#场景 1:每日 UV 统计)

[场景 2:月度 UV 统计](#场景 2:月度 UV 统计)


BitMap

使用场景

在开发中,Bitmap 经常被用于以下场景:

1. 用户签到系统

场景描述

用户每天签到一次,系统需要记录用户每月的签到情况,并支持快速查询连续签到天数、总签到天数等。

实现方式

  • 使用一个 Bitmap,每一位代表一天(1表示签到,0表示未签到)。

  • 例如,用户ID为1的用户在2023年10月的签到记录可以用一个31位的 Bitmap 表示。

优点

  • 存储空间极小:一个月的签到记录只需要4字节(32位)。

  • 查询效率高:可以通过位运算快速计算连续签到天数、总签到天数等。


2. 用户行为标记

场景描述

系统需要标记用户是否完成了某些行为(例如是否阅读了某篇文章、是否参与了某个活动等)。

实现方式

  • 使用一个 Bitmap,每一位代表一个行为(1表示完成,0表示未完成)。

  • 例如,用户ID为1的用户完成了行为A、B、D,可以用 0b1101 表示。

优点

  • 节省存储空间:一个用户的所有行为标记可以用一个整数表示。

  • 支持快速查询:通过位运算可以快速判断用户是否完成了某个行为。


3. 布隆过滤器(Bloom Filter)

场景描述

布隆过滤器是一种概率型数据结构,用于快速判断某个元素是否存在于一个集合中(可能存在误判,但不会漏判)。

实现方式

  • 使用一个 Bitmap 作为布隆过滤器的底层存储结构。

  • 通过多个哈希函数将元素映射到 Bitmap 的不同位置,并将这些位置标记为1。

优点

  • 空间效率极高:适合海量数据的去重和查询。

  • 查询速度快:时间复杂度为 O(1)。

BitMap介绍

如果是使用表来储存,需要耗费大量的内存,数据库压力山大

因此我们换一种方式来存储,一个月最多有31天,因此,如果某一天签到了,那么对应的位为1,没有则为0。这种方式只需要31bit,也就是8字节,大大节省了空间。

Redis中的使用
Redis功能示例
添加:

储存为11100111

获取:
批量获取:

u2中的u表示储存的为无符号,2表示只截取两个比特位,截取结果为11,转化为十进制就是3

java中实现
java 复制代码
    public Result sign() {
        // 获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 获取日期
        LocalDateTime now = LocalDateTime.now();
        // 拼接用户和日期变成key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
//        String key = "sign:"+userId+keySuffix;
        String key = USER_SIGN_KEY+userId+keySuffix;
        // 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 写入Redis setbit key offset 1
        stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true); // 注意这里需要减一因为在储存中字节是从0开始的
        return Result.ok();
    }

统计本月连续签到次数

java 复制代码
    @Override
    public Result signCount() {
        // 获取登录用户
        Long userId = UserHolder.getUser().getId();
        // 获取日期
        LocalDateTime now = LocalDateTime.now();
        // 拼接用户和日期变成key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
//        String key = "sign:"+userId+keySuffix;
        String key = USER_SIGN_KEY+userId+keySuffix;
        // 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();

        //获取本月为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD key GET udayOfMonth 0
        List<Long> result = stringRedisTemplate.opsForValue().bitField(key,
                BitFieldSubCommands.create()
                        .get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)) // 子命令
                        .valueAt(0)
        );

        if(result == null || result.isEmpty()){
            return Result.ok(0);
        }
        //  为什么需要 get(0)?get(0) 是从 List<Long> 中获取第一个元素。
        //  stringRedisTemplate.opsForValue().bitField(...) 返回的是一个 List<Long>,
        //  即使你只请求了一个值,它也会以列表的形式返回。
        //  因此,result.get(0) 获取的是这个列表中的第一个元素,也就是你请求的签到记录的值。
        Long num = result.get(0);
        if(num == null || num == 0){
            return  Result.ok(0);
        }

        // 遍历循环
        int cnt = 0;
        while(cnt < dayOfMonth){
            if ((num & 1) == 0) {
                break;
            }
            cnt++;
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
            num >>>=1;
        }
        return Result.ok(cnt);

    }

UV统计

在 Redis 中,UV(Unique Visitor)统计 是指统计某个时间段内访问某个资源的独立用户数量。UV 统计是许多应用场景(如网站访问量统计、广告点击统计等)中的核心需求。Redis 提供了多种数据结构和方法来实现高效的 UV 统计。

以下是 Redis 中 UV 统计的相关知识点介绍:

UV 统计的核心需求

  • 去重:同一个用户在同一时间段内的多次访问只算作一次。

  • 高效存储:需要支持海量用户的统计。

  • 快速查询:能够快速获取某个时间段内的 UV 数据。

使用 HyperLogLog

原理

  • HyperLogLog 是一种概率算法,用于估算大量数据的基数(去重后的数量)。

  • 它通过极小的存储空间(每个 HyperLogLog 键只需要 12 KB)来统计 UV。

命令

  • PFADD key user_id:将用户 ID 添加到 HyperLogLog 中。

  • PFCOUNT key:获取 UV 的估算值。

优点

  • 存储空间极小,适合海量用户的 UV 统计。

  • 查询速度快。

缺点

  • 结果是估算值,存在一定的误差(标准误差约为 0.81%)

UV 统计的常见场景

场景 1:每日 UV 统计

需求

  • 统计每天的独立访问用户数。

实现

  • 使用 HyperLogLog,每天创建一个新的键(例如 uv:2023-10-01),将当天的用户 ID 添加到键中。

  • 每天结束时,使用 PFCOUNT 获取当天的 UV 值。

场景 2:月度 UV 统计

需求

  • 统计每月的独立访问用户数。

实现

  • 使用 HyperLogLog,将整个月的用户 ID 添加到同一个键中(例如 uv:2023-10)。

  • 每月结束时,使用 PFCOUNT 获取当月的 UV 值。

相关推荐
地球资源数据云13 小时前
1951-2025年中国逐年1千米逐月总降水量区域统计数据集_年表_县
大数据·数据结构·数据库·数据仓库·人工智能
l1t13 小时前
DeepSeek v4辅助生成的单文件SQL查询示例页面
javascript·数据库·sql
云飞云共享云桌面13 小时前
精密机械制造工厂研发部门使用SolidWorks和ug,三维设计云桌面如何选择?
大数据·运维·服务器·网络·数据库·人工智能·制造
志飞14 小时前
springboot配置可持久化本地缓存ehcache
java·spring boot·缓存·ehcache·ehcache持久化
IntMainJhy14 小时前
【flutter for open harmony】第三方库 Flutter 二维码生成的鸿蒙化适配与实战指南
数据库·flutter·华为·sqlite·harmonyos
それども14 小时前
Spring Bean 注入的优先级顺序
java·数据库·sql·spring
张子行的博客14 小时前
SQL 调优实战:跨表排序性能提升之路
数据库·sql·oracle
Irene199114 小时前
数据发散(Data Spreading)详解(附:示例 数据发散最大值是笛卡尔乘积)
数据库
a95114164214 小时前
c++如何解析二进制协议中的可选字段读取逻辑及其反序列化【详解】
jvm·数据库·python
weixin_5806140014 小时前
golang如何实现时间格式化_golang时间格式化方法详解
jvm·数据库·python