微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计功能

目录
  • 引言
  • 一、Redis BitMap 基本用法
    • BitMap 基本语法、指令
    • 使用 BitMap 完成功能实现
  • 二、SpringBoot 整合 Redis 实现签到 功能
    • ️需求介绍
    • 核心源码
  • 三、SpringBoot 整合Redis 实现 签到统计功能
  • 四、关于使用bitmap来解决缓存穿透的方案
    • 小结

引言

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

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

一、Redis BitMap 基本用法

BitMap 基本语法、指令

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

用户一次签到,就是一条记录,假如有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 命令

新增key 进行存储

查询 GETBIT命令

查看指定坐标的签到状态

查询 BITFIELD

无符号查询

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

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

️需求介绍

采用BitMap实现签到功能

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

思路分析:

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

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

说明
请求方式 POST
请求路径 /user/sign
请求参数
返回值

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

核心源码

UserController

@PostMapping("sign")
public Result sign() {
    return userService.sign();
}

UserServiceImpl

public Result sign() {
    //1. 获取登录用户
    Long userId = UserHolder.getUser().getId();
    //2. 获取日期
    LocalDateTime now = LocalDateTime.now();
    //3. 拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    //4. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    //5. 写入redis setbit key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth -1, true);
    return Result.ok();
}

接口进行测试

ApiFox进行测试

查看Redis 数据

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

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

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

逻辑分析:

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

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

BITFIELD key GET u[dayOfMonth] 0

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

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

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

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

需求:

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

说明请求方式GET请求路径/user/sign/count请求参数无返回值连续签到的天数

说明
请求方式 GET
请求路径 /user/sign/count
请求参数
返回值 连续签到的天数

核心源码

UserController

@GetMapping("/signCount")
public Result signCount() {
    return userService.signCount();
}

UserServiceImpl

public Result signCount() {
    //1. 获取登录用户
    Long userId = UserHolder.getUser().getId();
    //2. 获取日期
    LocalDateTime now = LocalDateTime.now();
    //3. 拼接key
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    String key = RedisConstants.USER_SIGN_KEY + userId + keySuffix;
    //4. 获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    //5. 获取本月截至今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202301 GET u3 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);
    }
    Long num = result.get(0);
    if (num == null || num == 0) {
        return Result.ok(0);
    }
    //6. 循环遍历
    int count = 0;
    while (true) {
        //6.1 让这个数字与1 做与运算,得到数字的最后一个bit位 判断这个数字是否为0
        if ((num & 1) == 0) {
            //如果为0,签到结束
            break;
        } else {
            count ++;
        }
        num >>>= 1;
    }
    return Result.ok(count);
}

进行测试

查看 Redis 变量

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

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

回顾缓存穿透

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

解决方案:

  • 判断id<0
  • 数据库为空的话,向redis里边把这个空数据缓存起来

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

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

所以我们如何解决呢?

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

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

哪怕你单独去提取这个主键,但是在 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则表明这一位上的数据一定不存在,采用这种方式来处理,需要重点考虑一个事情,就是误差率,所谓的误差率就是指当发生哈希冲突的时候,产生的误差。

小结

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

到此这篇关于微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计的文章就介绍到这了,更多相关Spring Boot 整合 Redis BitMap 实现 签到与统计内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Redis基于Bitmap实现用户签到功能

    目录 功能分析 更多应用场景 总结 参考资料 很多应用上都有用户签到的功能,尤其是配合积分系统一起使用.现在有以下需求: 签到1天得1积分,连续签到2天得2积分,3天得3积分,3天以上均得3积分等. 如果连续签到中断,则重置计数,每月重置计数. 显示用户某月的签到次数和首次签到时间. 在日历控件上展示用户每月签到,可以切换年月显示. ... 功能分析 对于用户签到数据,如果直接采用数据库存储,当出现高并发访问时,对数据库压力会很大,例如双十一签到活动.这时候应该采用缓存,以减轻数据库的压力,Re

  • Redis中统计各种数据大小的方法

    如果 MySQL 数据库比较大的话,我们很容易就能查出是哪些表占用的空间:不过如果 Redis 内存比较大的话,我们就不太容易查出是哪些(种)键占用的空间了. 有一些工具能够提供必要的帮助,比如 redis-rdb-tools 可以直接分析 RDB 文件来生成报告,可惜它不能百分百实现我的需求,而我也不想在它的基础上二次开发.实际上开发一个专用工具非常简单,利用 SCAN和 DEBUG等命令,没多少行代码就能实现: 复制代码 代码如下: <?php $patterns = array(    

  • 基于Redis位图实现用户签到功能

    场景需求 适用场景如签到送积分.签到领取奖励等,大致需求如下: 签到1天送1积分,连续签到2天送2积分,3天送3积分,3天以上均送3积分等. 如果连续签到中断,则重置计数,每月初重置计数. 当月签到满3天领取奖励1,满5天领取奖励2,满7天领取奖励3--等等. 显示用户某个月的签到次数和首次签到时间. 在日历控件上展示用户每月签到情况,可以切换年月显示--等等. 设计思路 对于用户签到数据,如果每条数据都用K/V的方式存储,当用户量大的时候内存开销是非常大的.而位图(BitMap)是由一组bit

  • java redis 实现简单的用户签到功能

    业务需求是用户每天只能签到一次,而且签到后用户增加积分,所以把用户每次签到时放到redis 缓存里面,然后每天凌晨时再清除缓存,大概简单思想是这样的 直接看代码吧如下 @Transactional @Override public void signIn(Integer memberId) throws BizException { if(memberId==null){ throw new BizException(ErrorCode.BIZ_EXCEPTION.getErrcode(), "

  • 利用Redis统计网站在线活跃用户的方法

    前言 在工作中我们经常遇到这样的需求,要对某个在线网站的活跃用户数量进行统计.这里我们以redis为例,说明一下其实现的过程. 实现方法 在Redis中存在bitmap这种数据类型,这种数据类型是建立在string数据类型之上的.这里,我们主要用到setbit.bitcount这2个命令,而使用的客户端为python的redis库. import redis r = redis.StrictRedis(host="127.0.0.1",port=6379,db=0) 这里我们引入red

  • PHP使用redis位图bitMap 实现签到功能

    一.需求 记录用户签到,查询用户签到 二.技术方案 1.使用mysql(max_time字段为连续签到天数) 思路: (1)用户签到,插入一条记录,根据create_time查询昨日是否签到,有签到则max_time在原基础+1,否则,max_time=0 (2)检测签到,根据user_id.create_time查询记录是否存在,不存在则表示未签到 2.使用redis位图功能 思路: (1)每个用户每个月单独一条redis记录,如00101010101010,从左往右代表01-31天(每月有几

  • SpringBoot整合Redis实现访问量统计的示例代码

    目录 前言 Spring Boot 整合 Redis 引入依赖.增加配置 翠花!上代码 前言 之前开发系统的时候客户提到了一个需求:需要统计某些页面的访问量,记得当时还纠结了一阵子,不知道怎么去实现这个功能,后来还是在大佬的带领下借助 Redis 实现了这个功能.今天又回想起了这件事,正好和大家分享一下 Spring Boot 整合 Redis 实现访问量统计的全过程. 首先先解释一下为什么需要借助 Redis,其实原因也很简单,就是因为它非常快(每秒可执行大约110000次的 SET 操作,每

  • SpringBoot使用Redis的zset统计在线用户信息

    统计在线用户的数量,是应用很常见的需求了.如果需要精准的统计到用户是在线,离线状态,我想只有客户端和服务器通过保持一个TCP长连接来实现.如果应用本身并非一个IM应用的话,这种方式成本极高. 现在的应用都趋向于使用心跳包来标识用户是否在线.用户登录后,每隔一段时间,往服务器推送一个消息,表示当前用户在线.服务器则可以定义一个时间差,例如:5分钟内收到过客户端心跳消息,视为在线用户. 在线用户统计的实现 基于数据库实现 最简单的办法,就是在用户表,添加一个最后心跳包的日期时间字段 last_act

  • 微服务Spring Boot 整合 Redis 实现好友关注功能

    目录 引言 一.Redis 实现好友关注 – 关注与取消关注 二.Redis 实现好友关注 – 共同关注功能 小结 引言 本博文参考 黑马 程序员B站 Redis课程系列 在点评项目中,有这样的需求,如何实现笔记的好友关注.以及发布笔记后推送消息功能? 使用Redis 的 好友关注.以及发布笔记后推送消息功能 一.Redis 实现好友关注 – 关注与取消关注 需求:针对用户的操作,可以对用户进行关注和取消关注功能. 在探店图文的详情页面中,可以关注发布笔记的作者 具体实现思路:基于该表数据结构,

  • 微服务Spring Boot 整合Redis 阻塞队列实现异步秒杀下单思路详解

    目录 引言 一.秒杀优化 - 异步秒杀思路 二.秒杀优化 - 基于Redis完成秒杀资格判断 三.基于阻塞队列完成异步秒杀下单 四.测试程序 五.源码地址 引言 本章节,介绍使用阻塞队列实现秒杀的优化,采用异步秒杀完成下单的优化! 一.秒杀优化 - 异步秒杀思路 当用户发起请求,此时会请求nginx,nginx会访问到tomcat,而tomcat中的程序,会进行串行操作,分成如下几个步骤 查询优惠卷 判断秒杀库存是否足够 查询订单 校验是否是一人一单 扣减库存 创建订单,完成 在以上6个步骤中,

  • Spring Boot 整合Redis 实现优惠卷秒杀 一人一单功能

    目录 一.什么是全局唯一ID 全局唯一ID Redis实现全局唯一ID 二.环境准备 三.实现秒杀下单 四.库存超卖问题 问题分析 乐观锁解决库存超卖 Jmeter 测试 五.优惠卷秒杀 实现一人一单 小结 一.什么是全局唯一ID 全局唯一ID 在分布式系统中,经常需要使用全局唯一ID查找对应的数据.产生这种ID需要保证系统全局唯一,而且要高性能以及占用相对较少的空间. 全局唯一ID在数据库中一般会被设成主键,这样为了保证数据插入时索引的快速建立,还需要保持一个有序的趋势. 这样全局唯一ID就需

  • spring boot整合redis实现RedisTemplate三分钟快速入门

    引入依赖 <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-data-redis</artifactId> </dependency> RedisTemplate五种数据结构的操作 redisTemplate.opsForValue(); //操作字符串 redisTemplate.opsForHash();

  • Spring Boot整合Redis的完整步骤

    前言 实际 开发 中 缓存 处理是必须的,不可能我们每次客户端去请求一次 服务器 ,服务器每次都要去 数据库 中进行查找,为什么要使用缓存?说到底是为了提高系统的运行速度.将用户频繁访问的内容存放在离用户最近,访问速度最 快的 地方,提高用户的响 应速度,今天先来讲下在 springboot 中整合 redis 的详细步骤. 一.Spring Boot对Redis的支持 Spring对Redis的支持是使用Spring Data Redis来实现的,一般使用Jedis或者lettuce(默认),

  • spring boot整合redis实现shiro的分布式session共享的方法

    我们知道,shiro是通过SessionManager来管理Session的,而对于Session的操作则是通过SessionDao来实现的,默认的情况下,shiro实现了两种SessionDao,分别为CachingSessionDAO和MemorySessionDAO,当我们使用EhCache缓存时,则是使用的CachingSessionDAO,不适用缓存的情况下,就会选择基于内存的SessionDao.所以,如果我们想实现基于Redis的分布式Session共享,重点在于重写Session

  • Spring boot 整合 Redisson实现分布式锁并验证功能

    目录 简述 1. 在idea中新建spring boot工程并引入所需依赖 2. 编写相关代码实现 3. 模拟实际环境验证 3.1 下载idea的docker插件并配置相关镜像信息 3.2 将spring boot打包的jar构建为docker镜像 3.2 配置nginx 3.3 下载安装Jmeter进行测试 简述 整篇文章写的比较粗糙,大佬看了轻喷.前半部分 是整合spring boot和redisson, 后半部分是验证分布式锁.在整个过程中遇见了不少的问题,在此做个记录少走弯路 redis

  • spring boot整合redis主从sentinel方式

    目录 springboot整合redis主从sentinel 一主二从三sentinel配置 新建springboot工程,并加入Redis依赖 工程结构 修改application.properties配置文件 新建Redis服务 测试类 测试结果 redis哨兵模式sentinel与springboot集成 安装Redis集群 springboot整合redis主从sentinel 一主二从三sentinel配置 1.master:127.0.0.1:6379 2.slave1:127.0.

  • Spring Boot整合 NoSQL 数据库 Redis详解

    目录 引言 一.环境准备 二.构建Spring Boot项目 三.引入Redis依赖 四.Reds相关配置 五.添加Redis配置类 六.测试一下 引言 在日常的开发中,除了使用 Spring Boot 这个企业级快速构建项目的框架之外,随着业务数据量的大幅度增加,对元数据库造成的压力成倍剧增.在此背景下, Redis 这个 NoSQL 数据库已然整个项目架构中的不可或缺的一部分,懂得如何 Spring Boot 整合 Redis ,是当今开发人员必备的一项技能,接下来对整合步骤进行详细说明.

  • Spring Boot整合Spring Cache及Redis过程解析

    这篇文章主要介绍了Spring Boot整合Spring Cache及Redis过程解析,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友可以参考下 1.安装redis a.由于官方是没有Windows版的,所以我们需要下载微软开发的redis,网址: https://github.com/MicrosoftArchive/redis/releases b.解压后,在redis根目录打开cmd界面,输入:redis-server.exe redis.wind

随机推荐