【Java开发】Redis位图实现统计日活周活月活

news2024/11/19 5:51:28

最近研究了使用 Redis 的位图功能统计日活周活等数据,特来和大家分享下,Redis 位图还可用于记录用户签到情况、判断某个元素是否存在于集合中等。

1 Redis 位图介绍

Redis 位图是一种特殊的数据结构,它由一系列位组成,每个位只能是0或1。在 Redis 中,位图可以用来存储和操作二进制数据。位图提供了一些特殊的命令,使得我们可以对位进行操作,如设置、清除、计数和查询等。

Redis位图的底层实现采用了稀疏数据结构,这意味着当位图中大部分位都是0时,Redis 只会占用很少的内存空间。这使得位图在处理大规模数据时非常高效。

简单来说,Redis 位图使用二进制减少了统计数据存储的内存,使用大用户规模的情况,理论层面就不多说了,接下来直接讲应用层面吧~

2 RedisTemplate 位图技术实现

Redis 位图操作有多种实现方式,比如 JedisRedisTemplateStringRedisTemplate 等,本文主要介绍 RedisTemplate 方式,特别简单易实现,StringRedisTemplate 其实和 RedisTemplate 技术实现一致,而 Jedis 比较麻烦了。

简单介绍一下 RedisTemplate,作为 Spring Data Redis 提供的 Redis 客户端工具。它封装了 Redis 的操作流程和异常处理流程,使得 Redis 操作更加简单方便,同时也提供了 Redis 常用数据结构的操作方式。RedisTemplate 主要提供了对 String、List、Set、ZSet、Hash 数据结构的操作,支持序列化和反序列化的方式存储数据,同时支持事务操作,具有高并发性能,是开发人员使用 Redis 必不可少的组件之一。

2.1 redis 依赖

除了 spring-boot 就是下边这个依赖了,用其他的依赖也可,此处只做参考:

<dependency>
    <groupId>org.springframework.boot</groupId>
    <artifactId>spring-boot-starter-data-redis-reactive</artifactId>
</dependency>

2.2 添加配置

① application.yml

配置类写上 redis 地址:

spring:
  redis:
    database: 0
    host: 172.0.0.1
    port: 6379
    password: xxxx

② FastJsonRedisSerializer

序列化配置类:

import com.alibaba.fastjson.JSON;
import com.alibaba.fastjson.parser.ParserConfig;
import com.alibaba.fastjson.serializer.SerializerFeature;
import com.fasterxml.jackson.databind.JavaType;
import com.fasterxml.jackson.databind.type.TypeFactory;
import org.springframework.data.redis.serializer.RedisSerializer;
import org.springframework.data.redis.serializer.SerializationException;

import java.nio.charset.Charset;
import java.nio.charset.StandardCharsets;

//Redis相关配置,Redis使用FastJson序列化
public class FastJsonRedisSerializer<T> implements RedisSerializer<T> {

    public static final Charset DEFAULT_CHARSET = StandardCharsets.UTF_8;

    private final Class<T> clazz;

    static {
        ParserConfig.getGlobalInstance().setAutoTypeSupport(true);
    }

    public FastJsonRedisSerializer(Class<T> clazz) {
        super();
        this.clazz = clazz;
    }

    @Override
    public byte[] serialize(T t) throws SerializationException {
        if (t == null) {
            return new byte[0];
        }
        return JSON.toJSONString(t, SerializerFeature.WriteClassName).getBytes(DEFAULT_CHARSET);
    }

    @Override
    public T deserialize(byte[] bytes) throws SerializationException {
        if (bytes == null || bytes.length <= 0) {
            return null;
        }
        String str = new String(bytes, DEFAULT_CHARSET);

        return JSON.parseObject(str, clazz);
    }


    protected JavaType getJavaType(Class<?> clazz) {
        return TypeFactory.defaultInstance().constructType(clazz);
    }
}

2.3 工具类实现位图操作

如下代码,省略了和位图没什么关系的方法,大家可任意使用以下方法:

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.data.redis.core.ValueOperations;
import org.springframework.stereotype.Component;

@Component
@SuppressWarnings(value = { "unchecked", "rawtypes" })
public class RedisCache {

    @Autowired
    public RedisTemplate redisTemplate;

    /**
     * 缓存基本的对象,Integer、String、实体类等
     *
     * @param key 缓存的键值
     * @param value 缓存的值
     */
    public <T> void setCacheObject(final String key, final T value) {
        redisTemplate.opsForValue().set(key, value);
    }


    /**
     * 获得缓存的基本对象。
     *
     * @param key 缓存键值
     * @return 缓存键值对应的数据
     */
    public <T> T getCacheObject(final String key) {
        ValueOperations<String, T> operation = redisTemplate.opsForValue();
        return operation.get(key);
    }


    /**
     * 设置位图数据
     * @param key 键
     * @param id
     * @param bool
     */
    public Boolean setBit(String key, long id, boolean bool){
        return redisTemplate.opsForValue().setBit(key, id, bool);
    }


    /**
     * 返回位图数据
     * @param key 键
     * @param id
     */
    public Boolean getBit(String key, long id){
        return redisTemplate.opsForValue().getBit(key, id);
    }


    /**
     * bitCount 统计值对应位为1的数量
     * @param key redis key
     */
    public Long bitCount(String key) {
        return (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes()));
    }


    /**
     * bitCount 统计值指定范围(范围为字节范围)对应位为1的数量
     * @param key redis key
     * @param start 开始字节位置(包含)
     * @param end 结束字节位置(包含)
     */
    public Long bitCount(String key, long start, long end) {
        return (Long) redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(key.getBytes(), start, end));
    }
}

 关于 redis 更多操作可参考:Docker 环境下安装 Redis 并连接 Spring 项目实现简单 CRUD

3 日活周活月活实践

3.1 日活

实现思路也是蛮简单的,第一点是确定 key,比如 20230923,那这就是该天的 key,第二点是确定 id,该 id 可以取自用户表的主键,也可用可唯一指定用户的数据替代。

以下是测试类实现:

@SpringBootTest(classes = Application.class)
@RunWith(SpringRunner.class)
public class test {

    @Autowired
    private RedisCache redisCache;

    @Test
    public void testDayActive(){
        // 设置日活数据,模拟用户访问后台
        SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
        String todayStr = dateFormat.format(new Date());//比如20230923
        redisCache.setBit(todayStr, 1, true);
        redisCache.setBit(todayStr, 5, true);

        // 统计当天日活数据,只会提取 true 的数量
        Long todayCount = redisCache.bitCount(todayStr);
        System.out.println(todayStr + "该天日活数据为:" + todayCount);
    }

}

控制台输出👇

如此,日活就可实现了~

3.2 周活月活

思路就是日活的 for 循环累加,月活也可如此~

    @Test
    public void testDayActive(){
        // 1.假设每天的数据已通过定时任务成功保存至redis

        // 2.获取这周的所有日期,如:[20230918, 20230919, 20230920, 20230921, 20230922, 20230923, 20230924]
        List<String> dateStrs = getWeekDay();

        long weekCount = 0L;
        for (String dateStr : dateStrs) {
            Long todayCount = redisCache.bitCount(dateStr);
            weekCount = weekCount + todayCount;
        }

        System.out.println("当前周日活数据为:" + weekCount);
    }


    public static List<String> getWeekDay() {
        Calendar calendar = Calendar.getInstance();
        while (calendar.get(Calendar.DAY_OF_WEEK) != Calendar.MONDAY) {
            calendar.add(Calendar.DAY_OF_WEEK, -1);
        }
        List<Date> dates = new ArrayList<>(7);
        for (int i = 0; i < 7; i++) {  // i < 7 星期日
            dates.add(i, calendar.getTime());
            calendar.add(Calendar.DATE, 1);
        }

        List<String> dateStrs = new ArrayList<>();
        dates.forEach(date -> {
            SimpleDateFormat dateFormat = new SimpleDateFormat("yyyyMMdd");
            dateStrs.add(dateFormat.format(date));
        });

        return dateStrs;
    }

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1035796.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

Spring Cloud Alibaba Gateway 全链路跟踪TraceId日志

前言 凡是文中需要注册到nacos的都需要这个jar包 <dependency><groupId>com.alibaba.cloud</groupId><artifactId>spring-cloud-starter-alibaba-nacos-discovery</artifactId></dependency>凡是使用config jar包的都需要写bootstrap.prop…

多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测(SE注意力机制)

多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09; 目录 多维时序 | MATLAB实现WOA-CNN-BiLSTM-Attention多变量时间序列预测&#xff08;SE注意力机制&#xff09;预测效果基本描述模型描述程序设计参考资料 预测效果 基…

Tuya MQTT 标准协议是什么?

TuyaLink 协议是涂鸦 IoT 开发平台面向物联网开发领域设计的一种数据交换规范&#xff0c;数据格式为 JSON&#xff0c;主要用于设备端和涂鸦 IoT 开发平台的双向通信&#xff0c;更便捷地实现了设备端和平台之间的业务数据交互。 设备的通信方式也是多种多样的。无线通信方式…

什么是Redux?它的核心概念有哪些?

聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是Redux&#xff1f;⭐ 它的核心概念有哪些&#xff1f;⭐ 写在最后 ⭐ 专栏简介 前端入门之旅&#xff1a;探索Web开发的奇妙世界 欢迎来到前端入门之旅&#xff01;感兴趣的可以订阅本专栏哦&#xff01;这个专栏是为那些对Web开发…

【Go】rsrc不是内部或外部命令、无法将“rsrc”项识别为 cmdlet、函数、脚本文件或可运行程序的名称 解决方法

前言 想尝试用go创建一个桌面应用程序&#xff0c;然后查了下决定用 walk。 我们要先下载walk&#xff0c;这里 官方链接 按照官方文档&#xff0c;我们先用go get命令下载。 go get github.com/lxn/walk然后分别创建好了 main.go、main.manifest 文件&#xff0c;代码如下…

Web自动化框架中验证码识别处理全攻略,让测试更得心应手!

前言&#xff1a; 随着Web应用程序的不断发展&#xff0c;自动化测试已成为项目开发中必不可少的一环。然而&#xff0c;验证码的出现却经常会使自动化测试变得更具挑战性。为了解决这个问题&#xff0c;我们需要一种方法来自动识别和处理验证码&#xff0c;从而提高自动化测试…

Spring面试题11:什么是Spring的依赖注入

该文章专注于面试,面试只要回答关键点即可,不需要对框架有非常深入的回答,如果你想应付面试,是足够了,抓住关键点 面试官:说一说Spring的依赖注入 依赖注入(Dependency Injection)是Spring框架的一个核心特性,它是指通过外部容器将对象的依赖关系注入到对象中,从而…

在大厂做外包的软件测试工程师,如今发展怎么样了?

前言 不久前&#xff0c;许久没联系的大学室友出差来了魔都&#xff0c;趁此机会叫上了昔日的两三好友&#xff0c;一起小酌了一杯&#xff0c;觥筹交错中&#xff0c;得知了大学室友今年开始和朋友一同创业&#xff0c;时隔多年终于从外包公司&#xff0c;成为了一名“正式员…

RDMA编程杂记

目录 编程杂记什么是P_Key建链基于Socket API的建链基于CM API的建链 编程杂记 什么是P_Key P_Key&#xff08;Partition Key&#xff09;用于提供InfiniBand网络的隔离机制&#xff0c;只有在一个分区内的节点可以互相通信。 P_Key是一个16位的值&#xff0c;有两部分 msb…

第五章React路由

文章目录 一、React的基本使用1-1、react-router-dom1-1-1、react-router-dom基本使用 1-2、一般组件与路由组件1-3、NavLink1-4、封装自己的组件--MyNavLink1-5、Switch的使用1-6、多级地址样式失效问题1-7、模糊匹配1-8、严格匹配1-9、Redirect重定向1-10、嵌套路由1-11、向路…

Spring学习笔记3 Spring对IOC的实现

Spring学习笔记2 Spring的入门程序_biubiubiu0706的博客-CSDN博客 控制反转是一种思想 控制反转是为了降低程序耦合度,提高程序扩展力,达到OCP(开闭原则)原则,达到DIP(依赖倒置原则)原则 控制反转,反转的是什么? 1.将对象的创建权力交出去,交给第三方容器负责 2.对象与对象…

1990-2021年全国31个省人口相关统计面板数据

1990-2021年全国31个省人口相关统计面板数据 1、时间&#xff1a;1990-2021年 2、来源&#xff1a;各省统计年鉴 3、指标&#xff1a;省份代码、年份、省份名称、省份名称、常住人口数、年末常住人口 0-14岁人口数、15-64岁人口数、65岁及以上人口数、6岁及以上人口&#x…

【精品资源】Java毕业设计攻略:从选题到答辩,一站式指南

导读&#xff1a; Java毕业设计是计算机科学与技术专业学生展示其编程能力、问题解决能力和创新思维的重要环节。这篇博客将为您提供一站式的Java毕业设计攻略&#xff0c;帮助您从选题到答辩&#xff0c;顺利完成毕业设计。 一、选题阶段 寻找灵感&#xff1a; 探讨热门技术如…

【Linux】Linux环境基础开发工具使用

Linux安装软件: 源代码安装rpm安装-- Linux安装包yum安装–解决安装源&#xff0c;安装版本&#xff0c;安装依赖 目录 一、Linux 软件包管理器 yum1.查看安装包2.安装软件3.删除软件一些有趣的软件的安装&#x1f47b;1.sl 二、vim编辑器1.使用vim1.模式转换2.命令模式2.底行模…

软件设计模式系列之十三——享元模式

1 模式的定义 享元模式&#xff08;Flyweight Pattern&#xff09;是一种结构型设计模式&#xff0c;它旨在减少内存占用或计算开销&#xff0c;通过共享大量细粒度对象来提高系统的性能。这种模式适用于存在大量相似对象实例&#xff0c;但它们的状态可以外部化&#xff08;e…

人工神经网络ANN:数学总结

一、内容 径向基函数&#xff08;Radial basis function&#xff0c;RBF&#xff09;&#xff1a;一个取值仅依赖于到原点距离的实值函数&#xff0c;即。此外&#xff0c;也可以按到某一中心点c的距离来定义&#xff0c;即。 可以用于许多向函基数的和来逼近某一给定的函数&a…

SpringBoot使用@Async异步注解

首先&#xff0c;想一想为什么使用异步线程? 举个例子: 当我们请求这个接口的时候,在接口调用了method这个方法 然而被调用的方法执行了一个线程睡眠三秒 因为method方法睡眠了三秒钟,所以这个接口响应的时间肯定是大于三秒。因为接口是从上往下执行的,首先会在控制台输出一…

微信小程序python+nodejs+php+springboot+vue 讲座预约系统

讲座预约管理系统的用户是系统最根本使用者&#xff0c;按需要分析系统包括用户&#xff1a;学生、管理员。 管理员通过后台的登录页面&#xff0c;选择管理员权限后进行登录&#xff0c;管理员的权限包括学生信息管理和文章公告管理。讲座公告管理&#xff0c;添加讲座公告信息…

Swift SwiftUI 隐藏键盘

如果仅支持 iOS 15 及更高版本&#xff0c;则可以通过聚焦和取消聚焦来激活和关闭文本字段的键盘。 在最简单的形式中&#xff0c;这是使用 FocusState 属性包装器和 focusable() 修饰符完成的-第一个存储一个布尔值&#xff0c;用于跟踪第二个当前是否被聚焦。 Code struct C…

洛谷P8815:逻辑表达式 ← CSP-J 2022 复赛第3题

【题目来源】https://www.luogu.com.cn/problem/P8815https://www.acwing.com/problem/content/4733/【题目描述】 逻辑表达式是计算机科学中的重要概念和工具&#xff0c;包含逻辑值、逻辑运算、逻辑运算优先级等内容。 在一个逻辑表达式中&#xff0c;元素的值只有两种可能&a…