SpringBoot+Redis Bitmap实现活跃用户统计

 更新时间:2023年11月17日 10:40:25   作者:myprince003  
Redis的Bitmap数据结构是一种紧凑的位图,它可以用于实现各种场景,其中统计活跃用户是一种经典的业务场景,下面我们就来学习一下SpringBoot如何利用Redis中的Bitmap实现活跃用户统计吧

前言

Redis的Bitmap数据结构是一种紧凑的位图,它可以用于实现各种场景,其中统计活跃用户是一种经典的业务场景。

实现原理是,通过将每个用户表示为一个位,从而跟踪用户的活跃状态,使用位图记录用户每天是否登录,并计算月度或年度活跃用户数。

案例代码

以下是一个小例子,可以看到,使用SpringDataRedis,可以很轻松的实现BitMap的操作。

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.BitSet;

@Service
public class UserLoginService {

    // 用户登录记录的键前缀
    private static final String LOGIN_KEY_PREFIX = "login:"; 
    // 月份格式化器
    private static final DateTimeFormatter MONTH_FORMATTER = DateTimeFormatter.ofPattern("yyyyMM"); 
    // 年份格式化器
    private static final DateTimeFormatter YEAR_FORMATTER = DateTimeFormatter.ofPattern("yyyy"); 

    @Autowired
    private RedisTemplate<String, Object> redisTemplate;

    /**
     * 记录用户登录
     *
     * @param userId 用户ID
     */
    public void recordLogin(String userId) {

        // 获取存储当天用户登录信息的键
        String loginKey = getLoginKey(); 
        // 计算位图偏移量,对应用户ID的哈希码
        int bitOffset = getUserIdHashCode(userId); 
        // 将用户ID对应的位设置为1,表示用户登录
        redisTemplate.opsForValue().setBit(loginKey, bitOffset, true); 
    }

    /**
     * 获取月度活跃用户统计数据
     *
     * @return 月度活跃用户统计数据
     */
    public BitSet getMonthlyActiveUsers() {

        // 获取存储月度活跃用户信息的键
        String monthlyKey = getMonthlyKey(); 
        // 从Redis中获取位图数据
        return retrieveBitSet(monthlyKey); 
    }

    /**
     * 获取年度活跃用户统计数据
     *
     * @return 年度活跃用户统计数据
     */
    public BitSet getYearlyActiveUsers() {

        // 获取存储年度活跃用户信息的键
        String yearlyKey = getYearlyKey(); 
        // 从Redis中获取位图数据
        return retrieveBitSet(yearlyKey); 
    }

    /**
     * 获取存储当天用户登录信息的键
     */
    private String getLoginKey() {

        LocalDate today = LocalDate.now();
        String dateKey = today.format(DateTimeFormatter.ISO_DATE);
        return LOGIN_KEY_PREFIX + dateKey;
    }

    /**
     * 计算用户ID的哈希码,保证非负数并适应位图长度
     */
    private int getUserIdHashCode(String userId) {

        int hashCode = userId.hashCode();
        // 1073741823是Redis位图最大支持长度(2^30-1),可根据实际需求调整
        return Math.abs(hashCode % 1073741823); 
    }

    /**
     * 获取存储月度活跃用户信息的键
     */
    private String getMonthlyKey() {

        LocalDate today = LocalDate.now();
        String monthKey = today.format(MONTH_FORMATTER);
        return LOGIN_KEY_PREFIX + "monthly:" + monthKey;
    }

    /**
     * 获取存储年度活跃用户信息的键
     */
    private String getYearlyKey() {

        LocalDate today = LocalDate.now();
        String yearKey = today.format(YEAR_FORMATTER);
        return LOGIN_KEY_PREFIX + "yearly:" + yearKey;
    }

    /**
     * 从Redis中获取位图数据
     */
    private BitSet retrieveBitSet(String key) {

        // 获取存储在Redis中的位图数据
        byte[] bytes = (byte[]) redisTemplate.opsForValue().get(key); 
        if (bytes != null) {
            // 将字节数组转换为BitSet
            return BitSet.valueOf(bytes); 
        } else {
            // 若不存在,则返回空的BitSet
            return new BitSet(); 
        }
    }
}

其中有几个地方解释一下:

1、recordLogin方法用于记录用户登录情况。每天的登录情况被保存在以"login:日期"为键的位图中,用户的登录状态由位图中对应的位表示;

2、countMonthlyActiveUsers方法用于计算月度活跃用户数量。每个月的活跃用户数保存在以"login:monthly:年月"为键的位图中,通过Redis的bitCount方法统计位图中置为1的位数,即月度活跃用户数;

3、ountYearlyActiveUsers方法用于计算年度活跃用户数量,原理同上,只是统计的键变为"login:yearly:年份";

4、getLoginKey、getUserIdHashCode、getMonthlyKey和getYearlyKey是辅助方法,负责生成对应的Redis键或计算用户ID的哈希码。

转换

上面的例子最终返回的是BitSet对象,通过这个对象我们经过转换后可以获取到许多经典的统计数据,这里列举一些经典常用的统计结果示例。

大家可以根据这里面列举的统计数据,针对上面的代码进行替换,得到自己想要的结果。

import java.util.BitSet;

// 获取月度活跃用户统计数据
BitSet monthlyActiveUsers = userLoginService.getMonthlyActiveUsers();

// 获取年度活跃用户统计数据
BitSet yearlyActiveUsers = userLoginService.getYearlyActiveUsers();

// 统计月度活跃用户数量
int monthlyActiveUserCount = monthlyActiveUsers.cardinality();

// 统计年度活跃用户数量
int yearlyActiveUserCount = yearlyActiveUsers.cardinality();

// 判断某个用户是否为月度活跃用户
String userId = "user123";
boolean isMonthlyActiveUser = monthlyActiveUsers.get(userLoginService.getUserIdHashCode(userId));

// 判断某个用户是否为年度活跃用户
boolean isYearlyActiveUser = yearlyActiveUsers.get(userLoginService.getUserIdHashCode(userId));

// 输出统计结果
System.out.println("月度活跃用户数量: " + monthlyActiveUserCount);
System.out.println("年度活跃用户数量: " + yearlyActiveUserCount);
System.out.println("用户user123是否为月度活跃用户: " + isMonthlyActiveUser);
System.out.println("用户user123是否为年度活跃用户: " + isYearlyActiveUser);

总结

Redis的Bitmap数据结构非常灵活,可以根据具体需求实现各种位操作,但平时在项目中很多人没有机会使用到,这个案例非常简单,希望能让大家对此有个认识,未来用到了不会感到陌生。

以上就是SpringBoot+Redis Bitmap实现活跃用户统计的详细内容,更多关于SpringBoot Redis Bitmap用户统计的资料请关注脚本之家其它相关文章!

相关文章

  • springboot自动重启的简单方法

    springboot自动重启的简单方法

    Springboot提供了热部署的方式,当发现任何类发生了改变,马上通过JVM类加载的方式,加载最新的类到虚拟机中。这篇文章主要介绍了springboot自动重启的实现方法,需要的朋友可以参考下
    2018-04-04
  • Java利用VLC开发简易视屏播放器功能

    Java利用VLC开发简易视屏播放器功能

    这篇文章主要介绍了Java利用VLC开发简易视屏播放器,本文给大家介绍的非常详细,对大家的学习或工作具有一定的参考借鉴价值,需要的朋友可以参考下
    2021-08-08
  • Spring中ThreadLocal的解析

    Spring中ThreadLocal的解析

    这篇文章主要介绍了Spring中ThreadLocal的解析,Spring通过各种DAO模板类降低了开发者使用各种数据持久技术的难度。这些模板类都是线程安全的,也就是说,多个DAO可以复用同一个模板实例而不会发生冲突,下面一起进入文章学子详细内容吧
    2022-01-01
  • 详解JavaEE 使用 Redis 数据库进行内容缓存和高访问负载

    详解JavaEE 使用 Redis 数据库进行内容缓存和高访问负载

    本篇文章主要介绍了JavaEE 使用 Redis 数据库进行内容缓存和高访问负载,具有一定的参考价值,有兴趣的可以了解一下
    2017-08-08
  • SpringBoot通过注解注入Bean的几种方式解析

    SpringBoot通过注解注入Bean的几种方式解析

    这篇文章主要为大家介绍了SpringBoot注入Bean的几种方式示例详解,有需要的朋友可以借鉴参考下,希望能够有所帮助,祝大家多多进步早日升职加薪
    2022-03-03
  • Java基础之Thymeleaf的简单使用

    Java基础之Thymeleaf的简单使用

    这篇文章主要介绍了Java基础之Thymeleaf的简单使用,文中有非常详细的代码示例,对正在学习java基础的小伙伴们有非常好的帮助,需要的朋友可以参考下
    2021-04-04
  • Java用jxl读取excel并保存到数据库的方法

    Java用jxl读取excel并保存到数据库的方法

    这篇文章主要为大家详细介绍了Java用jxl读取excel并保存到数据库的方法,具有一定的参考价值,感兴趣的小伙伴们可以参考一下
    2017-10-10
  • SpringSecurity 测试实战

    SpringSecurity 测试实战

    这篇文章主要介绍了SpringSecurity 测试实战,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值,需要的朋友们下面随着小编来一起学习学习吧
    2019-11-11
  • Spring Cloud  Eureka服务治理的实现

    Spring Cloud Eureka服务治理的实现

    服务治理是微服务框架中最为核心和基础的模块,它主要是用来实现各个微服务实例的自动化注册与发现。这篇文章主要介绍了Spring Cloud Eureka服务治理的实现,感兴趣的小伙伴们可以参考一下
    2018-06-06
  • 浅谈Java中的atomic包实现原理及应用

    浅谈Java中的atomic包实现原理及应用

    这篇文章主要介绍了浅谈Java中的atomic包实现原理及应用,涉及Atomic在硬件上的支持,Atomic包简介及源码分析等相关内容,具有一定借鉴价值,需要的朋友可以参考下。
    2017-12-12

最新评论