0
  • 聊天消息
  • 系统消息
  • 评论与回复
登录后你可以
  • 下载海量资料
  • 学习在线课程
  • 观看技术视频
  • 写文章/发帖/加入社区
会员中心
创作中心

完善资料让更多小伙伴认识你,还能领取20积分哦,立即完善>

3天内不再提示

如何利用SpringBoot+Redis BitMap实现签到与统计功能?

数据分析与开发 来源:CSDN 2023-10-25 16:41 次阅读

引言

在各个项目中,我们都可能需要用到签到和 统计功能。签到后会给用户一些礼品以此来吸引用户持续在该平台进行活跃。

签到功能,我们可以通过Redis中的 BitMap功能来实现

一、Redis BitMap 基本用法

BitMap 基本语法、指令

签到功能我们可以使用MySQL来完成,比如下表:

356e90ea-730f-11ee-939d-92fbcf53809c.png

用户一次签到,就是一条记录,假如有1000万用户,平均每人每年签到次数为10次,则这张表一年的数据量为 1亿条

每签到一次需要使用(8 + 8 + 1 + 1 + 3 + 1)共22 字节的内存,一个月则最多需要600多字节

这样的坏处,占用内存太大了,极大的消耗内存空间!

我们可以根据 Redis中 提供的 BitMap 位图功能来实现,每次签到与未签到用0 或1 来标识 ,一次存31个数字,只用了2字节 这样我们就用极小的空间实现了签到功能

BitMap 的操作指令:

SETBIT:向指定位置(offset)存入一个0或1

GETBIT:获取指定位置(offset)的bit值

BITCOUNT:统计BitMap中值为1的bit位的数量

BITFIELD:操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值

BITFIELD_RO:获取BitMap中bit数组,并以十进制形式返回

BITOP:将多个BitMap的结果做位运算(与 、或、异或)

BITPOS:查找bit数组中指定范围内第一个0或1出现的位置

使用 BitMap 完成功能实现

服务器Redis版本采用 6.2

进入redis查询 SETBIT 命令

35858bb0-730f-11ee-939d-92fbcf53809c.png

新增key 进行存储

358d90d0-730f-11ee-939d-92fbcf53809c.png

查询 GETBIT命令

359bdae6-730f-11ee-939d-92fbcf53809c.png

查看指定坐标的签到状态

35a6d612-730f-11ee-939d-92fbcf53809c.png

查询 BITFIELD

35b5189e-730f-11ee-939d-92fbcf53809c.png

无符号查询

35be7f06-730f-11ee-939d-92fbcf53809c.png

BITPOS 查询1 和 0 第一次出现的坐标

35cc7638-730f-11ee-939d-92fbcf53809c.png

二、SpringBoot 整合 Redis 实现签到 功能

需求介绍

采用BitMap实现签到功能

实现签到接口,将当前用户当天签到信息保存到Redis中

思路分析:

我们可以把 年和月 作为BitMap的key,然后保存到一个BitMap中,每次签到就到对应的位上把数字从0 变为1,只要是1,就代表是这一天签到了,反之咋没有签到。

实现签到接口,将当前用户当天签到信息保存至Redis中

35d70fb2-730f-11ee-939d-92fbcf53809c.png

提示:因为BitMap 底层是基于String数据结构,因此其操作都封装在字符串操作中了。

35e1b368-730f-11ee-939d-92fbcf53809c.png

核心源码

UserController

@PostMapping("sign")
publicResultsign(){
returnuserService.sign();
}

UserServiceImpl

publicResultsign(){
//1.获取登录用户
LonguserId=UserHolder.getUser().getId();
//2.获取日期
LocalDateTimenow=LocalDateTime.now();
//3.拼接key
StringkeySuffix=now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
Stringkey=RedisConstants.USER_SIGN_KEY+userId+keySuffix;
//4.获取今天是本月的第几天
intdayOfMonth=now.getDayOfMonth();
//5.写入redissetbitkeyoffset1
stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);
returnResult.ok();
}

接口进行测试

ApiFox进行测试

35ec24a6-730f-11ee-939d-92fbcf53809c.png

查看Redis 数据

35f0c25e-730f-11ee-939d-92fbcf53809c.png

三、SpringBoot 整合Redis 实现 签到统计功能

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

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

35fb3180-730f-11ee-939d-92fbcf53809c.png

逻辑分析:

获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据,就可以获得当前月的签到总天数了

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

BITFIELDkeyGETu[dayOfMonth]0

假设今天是7号,那么我们就可以从当前月的第一天开始,获得到当前这一天的位数,是7号,那么就是7位,去拿这段时间的数据,就能拿到所有的数据了,那么这7天里边签到了多少次呢?统计有多少个1即可。

问题三:如何从后向前遍历每个Bit位?

注意:bitMap返回的数据是10进制,哪假如说返回一个数字8,那么我哪儿知道到底哪些是0,哪些是1呢?

我们只需要让得到的10进制数字和1做与运算就可以了,因为1只有遇见1 才是1,其他数字都是0 ,我们把签到结果和1进行与操作,每与一次,就把签到结果向右移动一位,依次内推,我们就能完成逐个遍历的效果了。

需求:

实现以下接口,统计当前截至当前时间在本月的连续天数

3601bcda-730f-11ee-939d-92fbcf53809c.png

有用户有时间我们就可以组织出对应的key,此时就能找到这个用户截止这天的所有签到记录,再根据这套算法,就能统计出来他连续签到的次数了

核心源码

UserController

@GetMapping("/signCount")
publicResultsignCount(){
returnuserService.signCount();
}

UserServiceImpl

publicResultsignCount(){
//1.获取登录用户
LonguserId=UserHolder.getUser().getId();
//2.获取日期
LocalDateTimenow=LocalDateTime.now();
//3.拼接key
StringkeySuffix=now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
Stringkey=RedisConstants.USER_SIGN_KEY+userId+keySuffix;
//4.获取今天是本月的第几天
intdayOfMonth=now.getDayOfMonth();
//5.获取本月截至今天为止的所有的签到记录,返回的是一个十进制的数字BITFIELDsign202301GETu30
Listresult=stringRedisTemplate.opsForValue().bitField(
key,
BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));
//没有任务签到结果
if(result==null||result.isEmpty()){
returnResult.ok(0);
}
Longnum=result.get(0);
if(num==null||num==0){
returnResult.ok(0);
}
//6.循环遍历
intcount=0;
while(true){
//6.1让这个数字与1做与运算,得到数字的最后一个bit位判断这个数字是否为0
if((num&1)==0){
//如果为0,签到结束
break;
}else{
count++;
}
num>>>=1;
}
returnResult.ok(count);
}

进行测试

3613f5f8-730f-11ee-939d-92fbcf53809c.png

查看 Redis 变量

3618637c-730f-11ee-939d-92fbcf53809c.png

从今天开始,往前查询 连续签到的天数,结果为2 测试无误!

四、关于使用bitmap来解决缓存穿透的方案

回顾缓存穿透:

发起了一个数据库不存在的,redis里边也不存在的数据,通常你可以把他看成一个攻击

解决方案:

判断id<0

数据库为空的话,向redis里边把这个空数据缓存起来

第一种解决方案:遇到的问题是如果用户访问的是id不存在的数据,则此时就无法生效

第二种解决方案:遇到的问题是:如果是不同的id那就可以防止下次过来直击数据

所以我们如何解决呢?

我们可以将数据库的数据,所对应的id写入到一个list集合中,当用户过来访问的时候,我们直接去判断list中是否包含当前的要查询的数据,如果说用户要查询的id数据并不在list集合中,则直接返回,如果list中包含对应查询的id数据,则说明不是一次缓存穿透数据,则直接放行。

362735f0-730f-11ee-939d-92fbcf53809c.png

现在的问题是这个主键其实并没有那么短,而是很长的一个 主键

哪怕你单独去提取这个主键,但是在 11年左右,淘宝的商品总量就已经超过10亿个

所以如果采用以上方案,这个list也会很大,所以我们可以使用bitmap来减少list的存储空间

我们可以把list数据抽象成一个非常大的bitmap,我们不再使用list,而是将db中的id数据利用哈希思想,比如:

id 求余bitmap长度 :id % bitmap.size = 算出当前这个id对应应该落在bitmap的哪个索引上,然后将这个值从0变成1,然后当用户来查询数据时,此时已经没有了list,让用户用他查询的id去用相同的哈希算法, 算出来当前这个id应当落在bitmap的哪一位,然后判断这一位是0,还是1,如果是0则表明这一位上的数据一定不存在,采用这种方式来处理,需要重点考虑一个事情,就是误差率,所谓的误差率就是指当发生哈希冲突的时候,产生的误差。

3638de72-730f-11ee-939d-92fbcf53809c.png

图片

小结

以上就是对 微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计 的简单介绍,签到功能是很常用的,在项目中,是一个不错的亮点,统计功能也是各大系统中比较重要的功能,签到完成后,去统计本月的连续 签到记录,来给予奖励,可大大增加用户对系统的活跃度 技术改变世界!!!






审核编辑:刘清

声明:本文内容及配图由入驻作者撰写或者入驻合作网站授权转载。文章观点仅代表作者本人,不代表电子发烧友网立场。文章及其配图仅供工程师学习之用,如有内容侵权或者其他违规问题,请联系本站处理。 举报投诉
  • MySQL
    +关注

    关注

    1

    文章

    802

    浏览量

    26444
  • 逻辑分析
    +关注

    关注

    0

    文章

    13

    浏览量

    7979
  • Redis
    +关注

    关注

    0

    文章

    371

    浏览量

    10846

原文标题:SpringBoot+Redis BitMap 实现签到与统计功能

文章出处:【微信号:DBDevs,微信公众号:数据分析与开发】欢迎添加关注!文章转载请注明出处。

收藏 人收藏

    评论

    相关推荐

    Redis实战篇-16.用户签到-实现签到功能

    Redis
    电子学习
    发布于 :2023年01月07日 16:50:46

    Redis有序集合详细步骤

    利用Redis Sorted Set实现排行榜功能
    发表于 05-21 14:09

    在 Java 中利用 redis 实现一个分布式锁服务

    在 Java 中利用 redis 实现一个分布式锁服务
    发表于 07-05 13:14

    Spring boot中Redis的使用

    【本人秃顶程序员】springboot专辑:Spring boot中Redis的使用
    发表于 03-27 11:42

    浅析Redis的5种基本数据类型

    多余的话不多说,今天给大家带来的是 Redis 中的四种特殊的数据结构 bitmap,hyperLogLog,bloomFilter,GeoHash 。这四种数据结构其实有点类似于算法层面了,比如 GeoHash 其实就是一个 zset,
    的头像 发表于 05-05 23:44 2691次阅读
    浅析<b class='flag-5'>Redis</b>的5种基本数据类型

    Springboot+redis操作多种实现

    一、Jedis,Redisson,Lettuce三者的区别共同点:都提供了基于Redis操作的Java API,只是封装程度,具体实现稍有不同。 不同点: 1.1、Jedis 是Redis的Java
    的头像 发表于 09-22 10:48 1799次阅读
    <b class='flag-5'>Springboot+redis</b>操作多种<b class='flag-5'>实现</b>

    SpringBoot实现多线程

    SpringBoot实现多线程
    的头像 发表于 01-12 16:59 1795次阅读
    <b class='flag-5'>SpringBoot</b><b class='flag-5'>实现</b>多线程

    SpringBoot+Redis实现点赞功能的缓存和定时持久化(附源码)

    用户对浏览内容进行【点赞/取赞】,并发送【点赞/取赞】请求到后端,这些信息先存入Redis中缓存,再每隔两小时将Redis中的内容直接写入数据库持久化存储。
    的头像 发表于 02-09 16:38 4480次阅读

    Redis实现限流的三种方式分享

    当然,限流有许多种实现的方式,Redis具有很强大的功能,我用Redis实践了三种的实现方式,可以较为简单的
    的头像 发表于 02-22 09:52 1037次阅读

    基于SpringBoot+Redis的转盘抽奖

    基于SpringBoot+Redis等技术实现转盘抽奖活动项目,含前端、后台及数据库文件
    的头像 发表于 02-28 14:24 1497次阅读
    基于<b class='flag-5'>SpringBoot+Redis</b>的转盘抽奖

    如何在SpringBoot中解决Redis的缓存穿透等问题

    今天给大家介绍一下如何在SpringBoot中解决Redis的缓存穿透、缓存击穿、缓存雪崩的问题。
    的头像 发表于 04-28 11:35 708次阅读

    如何用Springboot整合Redis

    本篇文件我们来介绍如何用Springboot整合Redis。 1、Docker 安装 Redis 1.1 下载镜像 docker pull redis: 6 . 2 . 6 1.2 创
    的头像 发表于 10-08 14:56 559次阅读
    如何用<b class='flag-5'>Springboot</b>整合<b class='flag-5'>Redis</b>

    SpringBoot AOP + Redis 延时双删功能实战

    注意:要知道经常修改的数据表不适合使用Redis,因为双删策略执行的结果是把Redis中保存的那条数据删除了,以后的查询就都会去查询数据库。所以Redis使用的是读远远大于改的数据缓存。
    的头像 发表于 10-13 16:08 617次阅读
    <b class='flag-5'>SpringBoot</b> AOP + <b class='flag-5'>Redis</b> 延时双删<b class='flag-5'>功能</b>实战

    一个注解搞定SpringBoot接口防刷

    技术要点:springboot的基本知识,redis基本操作,
    的头像 发表于 11-28 10:46 386次阅读

    Redis工具集的实现和使用

    Redis 基本上是互联网公司必备的工具了,Redis的应用场景实在太多了,但是有很多相似的功能如果每个项目都要实现一遍就显得太麻烦了,所以为了方便,我打算开发一个基于
    的头像 发表于 12-03 17:32 1187次阅读
    <b class='flag-5'>Redis</b>工具集的<b class='flag-5'>实现</b>和使用