【Java开发】Redis位图轻松实现统计用户三日内留存数据

news2024/11/26 20:36:42

上一篇文章介绍了如何通过 Redis 位图实现统计日活周活月活,而 Redis 位图能做的远不止如此,本篇文章将介绍如何实现统计用户连续三日内登录的留存数据,从而更直观的反映软件的运营情况。

目录

1 实现思路

2 统计用户三日内留存数据

2.1  工具类

2.2  实践


1 实现思路

前提:已将用户访问后台的数据保存到 redis 中,流程如下图 👇

思路Redis 位图实际上是由二进制位组成的数据结构,那么我们讲位图原始数据拿到后可以进行解析,从而获取到所有偏移量为 1 的用户 id 的集合,依次类推,前天、昨天和今天的访问后台的用户 id 的集合都能拿到手,最后取交集即可~

如此,不仅能够得到连续三天登录用户的数量,还能拿到这些用户的 id

注:我采用的 id Mysql 数据库用户表的主键,且设置为自增。

2 统计用户三日内留存数据

依旧还是使用 RedisTemplate 来作为客户端工具!!

一些前置操作请点击该链接:Redis位图实现统计日活周活月活

2.1  工具类

相对于前一篇文章,新增了 listBoolTrue 静态方法,该方法就是用来返回某天登录用户 id 的集合。

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;


    /**
     * bitCount 获取所有值为1的偏移量--本文章用到的方法
     * @param key redis key
     */
    public List<Integer> listBoolTrue(String key) {
        List<Integer> result = new ArrayList<>();
        redisTemplate.execute((RedisCallback<Long>) con -> {
            byte[] bitmap = con.get(key.getBytes());
            if (bitmap != null){
                for (int offset = 0; offset < bitmap.length * 8; offset++) {
                    if ((bitmap[offset / 8] & (0x80 >> (offset % 8))) != 0) {
                        result.add(offset);
                    }
                }
            }
            return null;
        });
        return result;
    }

 
    /**
     * 缓存基本的对象,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));
    }
}

2.2  实践

思路还是蛮简单的,key 就是之前设置的,我这儿举个例子,最终返回的是取交集后的用户 id 的集合,如果只是统计数量,那么加个 size 就行了~

    @Test
    public void testRetainedUser(){
        // 获取三天登录用户id的集合
        List<Integer> todayUserIds = redisCache.listBoolTrue("20230930");
        List<Integer> yesterdayUserIds = redisCache.listBoolTrue("20231001");
        List<Integer> beforeYesterdayUserIds = redisCache.listBoolTrue("20231002");

        // 取交集
        todayUserIds.retainAll(yesterdayUserIds);
        todayUserIds.retainAll(beforeYesterdayUserIds);

        // 返回取交集后的结果
        System.out.println(todayUserIds);
    }

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

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

相关文章

[AIGC] “惊天神器!Java大师推荐的终极工具 Netty ,让你的代码速度狂飙!“

前言&#xff1a; 在现代网络技术中&#xff0c;高性能的网络传输和通信已经成为了一项非常重要的技能。而Netty作为一款高性能、异步事件驱动的网络应用框架&#xff0c;成为了Java开发者们的首选工具之一。作为一位Java大师&#xff0c;今天我将从三个方面&#xff08;是什么…

C++笔记之环形队列

C笔记之环形队列 code review! 文章目录 C笔记之环形队列1.概念I——摘自 https://mp.weixin.qq.com/s/HUn9TF09RZ-UJKYPR5ZXhA2.概念II——摘自 http://t.csdnimg.cn/72bng3.概念III—— 摘自https://mp.weixin.qq.com/s/9Ga502p1DLcc6o75JBQlDg4.概念IV—— 摘自https://mp…

Scala第十三章节

Scala第十三章节 1. 高阶函数介绍 2. 作为值的函数 3. 匿名函数 4. 柯里化 5. 闭包 6. 控制抽象 7. 案例: 计算器 scala总目录 文档资料下载

面试打底稿⑦ 项目一的第三部分

简历原文 抽查部分 完成路线规划模块选择路线功能&#xff0c;用neo4j这种存储图关系的非关系数据库&#xff0c;实现最短线路规划、最低成本线路规划 设计优化物流信息模块&#xff0c;合理选择数据库、缓存技术&#xff0c;实现数据精简、流量削峰、提高系统可 用性 模拟问答…

2023(2024届)计算机保研经验分享,圆梦山东大学

前言&#xff1a; Hello大家好&#xff0c;我是Dream&#xff0c;好久不见啦&#xff01;在这不见的半年多时间里我一直在全身心的投入保研之中&#xff0c;在写下这份面经时&#xff0c;真的是感慨颇多&#xff0c;思绪万千。站在这个时间点上&#xff0c;回首过去的几个月&am…

汽车电子——产品标准规范汇总和梳理(控制器)

文章目录 前言 一、电机控制 二、转向控制 三、制动控制 四、电池管理系统 五、充电系统 六、车身系统 七、通讯系统 总结 前言 见《汽车电子——产品标准规范汇总和梳理》 一、电机控制 《GB/T 18488.1-201X 电动汽车用电机及其控制器 第1部分&#xff1a;技术条件…

数字IC前端学习笔记:数字乘法器的优化设计(阵列乘法器)

相关阅读 数字IC前端https://blog.csdn.net/weixin_45791458/category_12173698.html?spm1001.2014.3001.5482 数字信号处理作为微处理器的核心部件&#xff0c;是决定着总体处理器性能的因素之一&#xff0c;而数字乘法器是最常见的一种数字信号处理电路。通常情况下&#…

AMBA总线APB、AHB、AXI(详细)总结附实例便于快速掌握

目录 一、简介二、具体内容2.1 APB2.2 AHB2.3 AXI 三、总线对比3.1 总体对比3.2 部分功能差异 四、其他相关链接1、PCI总线及发展历程总结2、SPI协议详细总结附实例图文讲解通信过程3、I2C总线内容总结分享 一、简介 本文主要介绍APB、AHB和AXI总线的相关内容&#xff0c;同时…

初级篇—第一章初识数据库

文章目录 为什么要使用数据库数据库与数据库管理系统数据库的相关概念数据库与数据库管理系统的关系 常用的数据库为什么如此多的厂商要选用MySQL&#xff1f;MySQL的目录 RDBMS 与 非RDBMS关系型数据库(RDBMS)非关系型数据库(非RDBMS) 关系型数据库设计规则表、记录、字段表的…

力扣:117. 填充每个节点的下一个右侧节点指针 II(Python3)

题目&#xff1a; 给定一个二叉树&#xff1a; struct Node {int val;Node *left;Node *right;Node *next; } 填充它的每个 next 指针&#xff0c;让这个指针指向其下一个右侧节点。如果找不到下一个右侧节点&#xff0c;则将 next 指针设置为 NULL 。 初始状态下&#xff0c;所…

Springboot+vue的在线试题题库管理系统(有报告),Javaee项目,springboot vue前后端分离项目。

演示视频&#xff1a; Springbootvue的在线试题题库管理系统&#xff08;有报告&#xff09;&#xff0c;Javaee项目&#xff0c;springboot vue前后端分离项目。 项目介绍&#xff1a; 本文设计了一个基于Springbootvue的前后端分离的在线试题题库管理系统&#xff0c;采用M&…

代码随想录算法训练营第五十二天 | 300. 最长递增子序列 674. 最长连续递增序列 718. 最长重复子数组

1. 最长递增子序列 300. 最长递增子序列 - 力扣&#xff08;LeetCode&#xff09; dp[i] 取决于 i 之前所有的dp class Solution {public int lengthOfLIS(int[] nums) {// dp[i] 第 0 - i 位的递增子序列长度int length nums.length;int[] dp new int[length];Arrays.fil…

基于 jasypt 实现spring boot 配置文件脱敏

前言 在项目构建过程中&#xff0c;保护敏感信息的安全性至关重要&#xff0c;为了提高系统的安全性能&#xff0c;我们采用了Jasypt来对配置文件中的敏感信息进行加密处理&#xff0c;以确保系统的机密信息不被轻易泄露。 步骤 添加Maven依赖 首先&#xff0c;我们需要添加…

秋招校招,什么是群面?

时间已经来到十月份&#xff0c;我们也迎来了秋季招聘的高峰期。一般来说&#xff0c;企业为了提高面试的速度&#xff0c;一般都会让我们进行群面。可是&#xff0c;很多人不懂得“群面”的意思&#xff0c;由此导致自己在面试环节丢分。今天&#xff0c;就跟着小编一起来了解…

Maven下载源码出现:Cannot download sources Sources not found for org.springframwork...

Maven下载源码出现&#xff1a;Cannot download sources Sources not found for org.springframwork… 最近重装了IDEA再次查看源码时发现总是报错&#xff0c;网上找了很多&#xff0c;发现解决方法都是在项目终端执行如下命令&#xff1a; mvn dependency:resolve -Dclassi…

【面试经典150 | 矩阵】矩阵置零

文章目录 写在前面Tag题目来源题目解读解题思路方法一&#xff1a; O ( m n ) O(mn) O(mn) 空间复杂度方法二&#xff1a; O ( m n ) O(mn) O(mn) 空间复杂度方法三&#xff1a;仅使用2个额外变量的常量空间复杂度 写在最后 写在前面 本专栏专注于分析与讲解【面试经典150】算…

网络工程师对口的岗位是什么

大家好&#xff0c;我是网络工程师成长日记实验室的郑老师&#xff0c;您现在正在查看的是网络工程师成长日记专栏&#xff0c;记录网络工程师日常生活的点点滴滴 网络工程师可以从事哪些职位呢&#xff1f;其实网络工程师最对应的最对口的这个岗位就是高级网络工程师、系统集成…

Centos一键安装、切换各版本JDK

查看服务中的安装的jdk rpm -qa | grep java获取jdk各版本信息 yum -y list java*查看指定版本 yum -y list java*|grep 1.8安装jdk yum install java-11-openjdk当服务器中有多个版本jdk&#xff0c;切换指定jdk版本 alternatives --config java按照提示输入编号即可切换&…

古诗词学习鉴赏APP设计与实现(源码+lw+部署文档+讲解等)

文章目录 前言具体实现截图论文参考详细视频演示为什么选择我自己的网站自己的小程序&#xff08;小蔡coding&#xff09;有保障的售后福利 代码参考源码获取 前言 &#x1f497;博主介绍&#xff1a;✌全网粉丝10W,CSDN特邀作者、博客专家、CSDN新星计划导师、全栈领域优质创作…

【Java基础】抽象类和接口的使用

个人主页&#xff1a;兜里有颗棉花糖 欢迎 点赞&#x1f44d; 收藏✨ 留言✉ 加关注&#x1f493;本文由 兜里有颗棉花糖 原创 收录于专栏【JavaSE_primary】 本专栏旨在分享学习JavaSE的一点学习心得&#xff0c;欢迎大家在评论区讨论&#x1f48c; 目录 一、抽象类抽象类概念…