基于Redis的BitMap实现签到、连续签到统计(含源码)

2023-11-12

微信公众号访问地址:基于Redis的BitMap实现签到、连续签到统计(含源码)

推荐文章:

    1、springBoot对接kafka,批量、并发、异步获取消息,并动态、批量插入库表;

    2、SpringBoot用线程池ThreadPoolTaskExecutor异步处理百万级数据;

    3、基于Redis的Geo实现附近商铺搜索(含源码)

    4、基于Redis实现关注、取关、共同关注及消息推送(含源码)

    5、SpringBoot整合多数据源,并支持动态新增与切换(详细教程)

    6、基于Redis实现点赞及排行榜功能

一、简介

基于Redis的BitMap相关命令,实现用户签到、连续签到统计等功能。

1.1、背景

分析:使用用一张表来存储用户签到信息,假如用户数量庞大,平均每人每年签到次数为 10 次,则这张表一年的数据量为 1 亿条,每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共 22 字节的内存,一个月则最多需要 600 多字节。

1.2、BitMap

        bitmap 不是一个独立的数据类型,而是一种特殊的 string 类型,它可以将一个 string 类型的值看作是一个由二进制位组成的数组,并提供了一系列操作二进制位的命令。一个 bitmap 类型的键最多可以存储 2^32 - 1 个二进制位。

         bitmap 类型的底层实现是 SDS(simple dynamic string),它和 string 类型相同,只是在操作时会将每个字节拆分成 8 个二进制位。

常见用法:

       Redis中的Bitmap是一种数据结构,用于存储和操作位数组(bit array)。它可以有效地表示指定范围内的位状态,每个位的值可以是0或1。使用Bitmap可以进行高效的位级别操作,例如对某个位进行设置、获取、翻转等操作,以及位的逻辑运算,如AND、OR、XOR等。

在Redis中,Bitmap的应用场景:

1、统计在线用户(签到):可以使用一个Bitmap,每个位代表一个用户ID,如果某个用户在线,则将对应位设置为1,否则设置为0。可以通过位操作来统计在线用户的数量。

2、频率统计:可以使用一个Bitmap,每个位代表一个事件,如果事件发生,则将对应位设置为1。可以通过位操作来统计某段时间内事件发生的频率。

3、实现布隆过滤器,利用 setbit 和 getbit 命令实现快速判断一个元素是否存在于一个集合中。

4、实现位图索引,利用 bitop 和 bitpos 命令实现对多个条件进行位运算和定位。

5、统计用户活跃度,利用 setbit 和 bitcount 命令实现每天或每月用户登录次数的统计

常用的命令:

        需要注意的是,由于Bitmap是以字节为单位存储的,因此对于较大的位图,可能会占用较多的内存。在使用Bitmap时,需要根据实际情况评估内存消耗。

1.3、BITFIELD使用说明

       Redis中的BITFIELD命令是用于对位域(bit field)进行操作的,位域是由多个位组成的数据结构。它允许你对位域进行读取、设置和计算等操作。下面是BITFIELD命令的用法示例:

bitfield bitfield_test get u4 0    #从第一个位开始取4个位(0110),结果为无符号数(u)结果:6bitfield bitfield_testget u3 2    #从第三个位开始取3个位(101),结果为无符号数(u)结果:5bitfield bitfield_testget get i4 0   #从第一个位开始取4个位(0110),结果为有符号数(i)结果:6因为结果为有符号数所以,第一位符号位为0代表是正数。110为6,结果直接为6bitfield bitfield_testget get i3 2   #从第三个位开始取3个位(101),结果为有符号数(i)结果:-3取到的结果首位为1代表是负数,01需要取补码运算。01取反为10,10+1为11。11十进制为3,因为符号位为1所以最终结果为-3

命令操作案例:

redis6.3:0>setbit qd_key 0 1"0"redis6.3:0>setbit qd_key 1 1"0"redis6.3:0>setbit qd_key 2 1"0"redis6.3:0>getbit qd_key 10"1"redis6.3:0>bitcount qd_key"6"redis6.3:0>bitfield qd_key get u2 01) "3"

二、签到功能实现

2.1、需求分析

2.2、代码实现

  public Result sign() {        // 1.获取当前登录用户//        Long userId = UserHolder.getUser().getId();        Long userId =999L;        // 2.获取日期 使用hutool的日期时间工具-DateUtil        Date date = DateUtil.date();        // 3.拼接key        String keySuffix = DateUtil.format(date, ":yyyyMM");        String key = USER_SIGN_KEY + userId + keySuffix;        // 4.获取今天是本月的第几天        int dayOfMonth =  DateUtil.dayOfMonth(date);        // 5.写入Redis SETBIT key offset 1        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);        return Result.ok();    }

结果展示:

三、连续签到统计功能实现

3.1、需求分析

问题1:什么叫做连续签到天数?

      从最后一次(当前时间)签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。

问题2:如何得到本月到今天为止的所有签到数据?

  BITFIELD key GET u[dayOfMonth]0

问题3:如何从后向前遍历每个bit位?

      与1做与运算,就能得到最后一个bt位。随后右移一位,下一个Bit位就成了最后一个Bit位。

3.2、代码实现

public Result signCount() {
        //        Long userId = UserHolder.getUser().getId();
        Long userId =999L;  //暂时写死
        // 2.获取日期 使用hutool的日期时间工具-DateUtil
        Date date = DateUtil.date();
        // 3.拼接key
        String keySuffix = DateUtil.format(date, ":yyyyMM");
        String key = USER_SIGN_KEY + userId + keySuffix;
        // 4.获取今天是本月的第几天
        int dayOfMonth =  DateUtil.dayOfMonth(date);
        // 5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:999:202308 GET u18 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);
        }
        //num为0,直接返回0
        Long num = result.get(0);
        if (num == null || num == 0) {
            return Result.ok(0);
        }
        // 6.循环遍历
        int count = 0;
        while (true) {
            // 6.1.让这个数字与1做与运算,得到数字的最后一个bit位  // 判断这个bit位是否为0
            if ((num & 1) == 0) {
                // 如果为0,说明未签到,结束
                break;
            }else {
                // 如果不为0,说明已签到,计数器+1
                count++;
            }
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
            num >>>= 1;
        }
        return Result.ok(count);
    }
 

3.3、结果展示

当天还没有签到统计:

当天已经签到统计:

四、源码获取方式

     更多优秀文章,请关注个人微信公众号或搜索“程序猿小杨”查阅。然后回复:源码,可以获取该项目对应的源码及表结构,开箱即可使用。

说明:后面redis相关操作的功能都会放在此文件夹中,需要相关功能的,只需要获取最新的资源,替换项目即可

       如果大家对相关文章感兴趣,可以关注微信公众号"程序猿小杨",会持续更新优秀文章!欢迎大家 分享、收藏、点赞、在看,您的支持就是我坚持下去的最大动力!谢谢!

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

基于Redis的BitMap实现签到、连续签到统计(含源码) 的相关文章

  • 如何在实时添加对象时从 Redis 中弹出对象?

    我想让 Node js 进程运行 因为它正在检查 Redis 服务器是否有任何新的弹出内容 另一个进程将偶尔进行推送 而 Node 进程将尝试弹出任何进来的内容 Node 进程将保持运行 有人能给我指出一个好的方向吗 我正在尝试找出如何监听
  • AWS Redis 从外部连接

    有没有办法从外部 AWS 网络连接 AWS 上托管的 Redis 实例 我有一个基于 Windows 的 EC2 实例在 AWS 上运行 另一个是 Redis 缓存节点 我知道有人问过这个问题 但答案是在基于 Linux 的系统中 但我的是
  • Spring RedisTemplate:8次调用后方法键挂起

    我使用 Spring RedisTemplate spring data redis 1 7 1 与 Redis 进行通信 我需要通过正则表达式获取然后删除键 例如 context user1 我用的方法 RedisTemplate key
  • Stackexchange.redis 缺乏“WAIT”支持

    我在客户端应用程序正在使用的负载均衡器后面有 3 个 Web API 服务器 我正在使用这个库来访问具有一个主服务器和几个从服务器的 Redis 集群 目前不支持 WAIT 操作 我需要此功能来存储新创建的用户会话并等待它复制到所有从属服务
  • 如何统计 Redis 流中未读或已确认的消息?

    使用 Redis 5 0 3 假设我们创建一个名为streamy和一个消费群体consumers XGROUP CREATE streamy consumers MKSTREAM 然后向其中添加一些消息 XADD streamy messa
  • 库存管理系统的 SQL 与 NoSQL

    我正在开发一个基于 JAVA 的网络应用程序 主要目的是拥有在多个称为渠道的网站上销售的产品的库存 我们将担任所有这些渠道的管理者 我们需要的是 用于管理每个渠道的库存更新的队列 库存表 其中包含每个通道上分配的正确快照 将会话 ID 和其
  • 是否有可嵌入的 Java 替代 Redis?

    根据这个线程 https stackoverflow com questions 3047010 best redis library for java 如果我想从Java中使用Redis Jedis是最好的选择 然而 我想知道是否有任何库
  • Redis hash写入速度非常慢

    我面临一个非常奇怪的问题 使用 Redis 时 我的写入速度非常糟糕 在理想的情况下 写入速度应该接近 RAM 上的写入速度 这是我的基准 package redisbenchmark import redis clients jedis
  • Spring Data Redis - Lettuce连接池设置

    尝试在 spring data redis 环境中设置 Lettuce 连接池 下面是代码 Bean LettuceConnectionFactory redisConnectionFactory GenericObjectPoolConf
  • Redis INCRBY 有限制

    我想知道是否有一种方法可以通过我的应用程序的单次往返在 Redis 中执行此操作 对于给定的键K 其可能值V是范围内的任意整数 A B 基本上 它有上限和下限 When an INCRBY or DECRBY发出命令 例如INCRBY ke
  • 从redis中检索大数据集

    一台服务器上的应用程序查询另一台服务器上运行的 Redis 查询的结果数据集约为 250kzrangebyscore objects locations inf inf这在应用程序服务器上似乎需要 40 秒 当使用命令执行时redis cl
  • 在 aws-elasticache 上使用 memcached 或 Redis

    我正在 AWS 上开发一个应用程序 并使用 AWS elasticache 进行缓存 我对使用 memcached 或 redis 感到困惑 我阅读了有关 redis 3 0 2 更新以及它现在如何等同于 memchached 的文章 ht
  • socket.io 广播功能 & Redis pub/sub 架构

    如果有人能帮助我解决一个小疑问 我将不胜感激 使用socket io广播功能和在Redis上使用pub sub设计架构有什么区别 例如 在另一个示例中 node js 服务器正在侦听 socket io 针对 键 模型 todo 和值 数据
  • 如何设置和获取Redis中存储的对象?

    我试图在 redis 中存储一个对象 当我获取该对象时 它似乎不起作用 I tried u User new u name blankman redis set test u x redis get test x name error 我想
  • 如何在Redis中从hmset()切换到hset()?

    我收到弃用警告 即 Redis hmset 已弃用 请改用 Redis hset 但是 hset 采用第三个参数 我不知道是什么name应该是 info users 10 timestamp datetime utcnow strftime
  • redis - 使用哈希

    我正在使用 redis 为我的 Web 应用程序实现社交流和通知系统 我是 redis 的新手 我对哈希值及其效率有一些疑问 我读过这篇很棒的文章Instagram 帖子 http instagram engineering tumblr
  • 如何使 Redis 缓存中数据层次结构(树)的部分内容无效

    我有一些产品数据 需要在 Redis 缓存中存储多个版本 数据由 JSON 序列化对象组成 获取普通 基本 数据的过程很昂贵 将其定制为不同版本的过程也很昂贵 因此我想缓存所有版本以尽可能进行优化 数据结构看起来像这样 BaseProduc
  • StackExchange.Redis Get 函数抛出 TimeoutException

    我在用着StackExchange Redis与 C 和StackExchangeRedisCacheClient Get函数抛出以下异常 myCacheClient Database StringGet txtKey Text myCac
  • 为什么Redis中没有有序的hashmap?

    Redis 数据类型 http redis io topics data types包括排序集 http redis io topics data types intro sorted sets以及其他用于键值存储的必要数据结构 但我想知道
  • redis 2.8.7 Linux Sentinel环境配置问题,如何使其自启动,应该订阅什么?

    现在我们尝试使用 redis 2 8 7 作为缓存存储 来自使用 booksleeve 客户端的 NET Web 应用程序 目前看来这是一个非常有趣和令人兴奋的任务 redis 文档非常好 但由于缺乏真正的实践经验 我确实有几个关于如何正确

随机推荐

  • Java 数据库连接池、线程池和对象池总结

    一 Java数据库连接池总结 数据库连接池的实现及原理 内容摘要 对于一个复杂的数据库应用 频繁的建立 关闭连接 会极大的减低系统的性能 因为对于连接的使用成了系统性能的瓶颈 有一个很著名的设计模式 资源池 该模式正是为了解决资源频繁分配
  • IDEA的下载安装及配置Tomcat

    IDEA的下载安装及配置tomcat 1 首先是下载及安装 IDEA的官方网站提供了两种安装包 一种是旗舰版 既Ultimate版和Community版 如上图 左边是旗舰版的 需要付费 但是可以破解 右边是社区版 是免费的 但是提供的功能
  • Merge sort(归并排序) -- 分治

    基本思路 确定分界点 mid l r 2 递归排序left right 将步骤2中排序好的left right数组进行归并 合二为一 C 代码实现 void merge sort int q int l int r if l gt r re
  • SQL-lab 38~53

    less38 本关卡为堆叠注入 注入语句为 id 1 CREATE DATABASE sq default charset utf8 查询用户名和密码 并创建数据库 数据库创建成功 说明两条语句都执行了 less39 45关 这几关与上一关
  • 第一次动手构建 Linux 内核

    目录 背景 机器参数 参考链接 操作流程 步骤1 下载 Linux 内核源码 步骤 2 解压源码 步骤 3 下载所需软件包 步骤 4 内核配置 步骤 5 开始构建 步骤 5 1 make 步骤 5 2 make INSTALL MOD ST
  • 多线程作业及答案

    多线程作业 一 填空题 1 处于运行状态的线程在某些情况下 如执行了sleep 睡眠 方法 或等待I O设备等资源 将让出CPU并暂时停止自己的运行 进入 状态 2 处于新建状态的线程被启动后 将进入线程队列排队等待CPU 此时它已具备了运
  • myeclipse无法打开工作空间

    现象 打开myeclipse工作空间时进度条不动 解决方式 找到工作空间的文件目录 如 D work 打开D work metadata plugins org eclipse core resources projects 目录 查找近期
  • Mysql入门到精通-快速插入1000万条数据(转)

    创建MyISAM模式表方便批量跑数据 CREATE TABLE logs1 id int 11 NOT NULL AUTO INCREMENT logtype varchar 255 DEFAULT NULL logurl varchar
  • SIFT解析(二)特征点位置确定

    最近微博上有人发起投票那篇论文是自己最受益匪浅的论文 不少人说是lowe的这篇介绍SIFT的论文 确实 在图像特征识别领域 SIFT的出现是具有重大意义的 SIFT特征以其稳定的存在 较高的区分度推进了诸多领域的发展 比如识别和配准 上一篇
  • 3月打卡活动第20天 面试题第40题:最小的k个数(简单)

    3月打卡活动第20天 面试题第40题 最小的k个数 简单 题目 输入整数数组 arr 找出其中最小的 k 个数 例如 输入4 5 1 6 2 7 3 8这8个数字 则最小的4个数字是1 2 3 4 解题思路 排序 取前k个值 class S
  • 常用人体模型关节索引

    SMPL 24 joints Cocoplus 19 joints 0 RAnkle 1 RKnee 2 RHip 3 LHip 4 LKnee 5 LAnkle 6 RWrist 7 RElbow 8 RShoulder 9 LShoul
  • 程序员们最恐惧的“代码”,一见就头疼,是老前辈留下来的代码!

    t行业是目前最赚钱的行业 程序员是目前最赚钱的职业之一 也是最容易过劳死的职业之一 虽然程序员们工资都很高 有的还会赚年薪 但是他们的工作真的是非常辛苦 因为程序员经常会熬夜写代码 在电脑前工作 他们脱发秃头的几率也非常高 由于程序员经常在
  • poco源码简单分析

    自动化工具poco源码简单分析 Airtest简介 Airtest是网易游戏开源的一款UI自动化测试项目 目前处于公开测试阶段 该项目分为AirtestIDE Airtest Poco Testlab四个部分 基于python脚本的方式 用
  • 【公告】博客专家 6 月发布原创/翻译文章奖励

    博客专家6月发布原创 翻译文章奖励 CSDN ID 所获奖励 malefactor 图灵社区技术图书 程序员杂志最新期刊 C币100 lmj623565791 图灵社区技术图书 程序员杂志最新期刊 C币100 jiangwei0910410
  • python TypeError: missing 1 required positional argument:'self'

    Python 调用类的函数时报错如下 TypeError seperate data missing 1 required positional argument self 报错原因 train data test data DataCle
  • 对spark dataframe join之后的列值NULL值进行填充为指定数值的操作

    众所周知 两个数据集如A B取JOIN操作的时候 其结果往往会出现NULL值的出现 这种情况是非常不利于后续的分析与计算的 特别是当涉及到对这个数值列进行各种聚合函数计算的时候 针对这种问题 当然从最简单的dataframe map来处理是
  • QThreadPool线程池的原理与使用

    一 为什么需要用线程池 现在所有的高性能服务器程序 几乎都会使用到线程池技术 从而更好且有效的榨干服务器性能 1 开多少个线程可以达到性能最佳 不知道 你有没有这个疑问 这是一种常见的线程使用方式 class MyThread public
  • list集合(接口)

    list集合 显而易见是用来存储数据的 可以把它看作是长度可变的数组 它是有序存储数据的 具有跟数组一样的索引 ArrayList LinkedList Vector Stack都是list接口的实现类 以ArrayList为例说明list
  • 1033 旧键盘打字 (20 分)

    题目 旧键盘上坏了几个键 于是在敲一段文字的时候 对应的字符就不会出现 现在给出应该输入的一段文字 以及坏掉的那些键 打出的结果文字会是怎样 输入格式 输入在 2 行中分别给出坏掉的那些键 以及应该输入的文字 其中对应英文字母的坏键以大写给
  • 基于Redis的BitMap实现签到、连续签到统计(含源码)

    微信公众号访问地址 基于Redis的BitMap实现签到 连续签到统计 含源码 推荐文章 1 springBoot对接kafka 批量 并发 异步获取消息 并动态 批量插入库表 2 SpringBoot用线程池ThreadPoolTaskE