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

上一篇文章介绍了如何通过 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);
    }
  • 2
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 2
    评论
要将 Redis 中的数据转存到 MySQL 中,你可以使用 Java 编写一个程序来实现。下面是一个简单的示例代码,供你参考: ```java import redis.clients.jedis.Jedis; import redis.clients.jedis.ScanParams; import redis.clients.jedis.ScanResult; import java.sql.Connection; import java.sql.DriverManager; import java.sql.PreparedStatement; import java.sql.SQLException; import java.util.Map; public class RedisToMysql { public static void main(String[] args) { // Redis 连接参数 String redisHost = "localhost"; int redisPort = 6379; String redisPassword = "your_redis_password"; int redisDbIndex = 0; // MySQL 连接参数 String mysqlHost = "localhost"; int mysqlPort = 3306; String mysqlDatabase = "your_database_name"; String mysqlUsername = "your_mysql_username"; String mysqlPassword = "your_mysql_password"; // 连接 Redis Jedis jedis = new Jedis(redisHost, redisPort); jedis.auth(redisPassword); jedis.select(redisDbIndex); // 连接 MySQL try (Connection conn = DriverManager.getConnection("jdbc:mysql://" + mysqlHost + ":" + mysqlPort + "/" + mysqlDatabase, mysqlUsername, mysqlPassword)) { // 遍历 Redis 中的所有键 ScanParams scanParams = new ScanParams().match("*"); ScanResult<String> scanResult; String cursor = "0"; do { scanResult = jedis.scan(cursor, scanParams); for (String key : scanResult.getResult()) { // 从 Redis 获取数据 Map<String, String> redisData = jedis.hgetAll(key); // 将数据插入到 MySQL String sql = "INSERT INTO your_table_name (key, field, value) VALUES (?, ?, ?)"; try (PreparedStatement stmt = conn.prepareStatement(sql)) { for (Map.Entry<String, String> entry : redisData.entrySet()) { stmt.setString(1, key); stmt.setString(2, entry.getKey()); stmt.setString(3, entry.getValue()); stmt.executeUpdate(); } } } cursor = scanResult.getStringCursor(); } while (!cursor.equals("0")); System.out.println("Data transfer completed!"); } catch (SQLException e) { e.printStackTrace(); } finally { jedis.close(); } } } ``` 请根据你的实际情况修改示例代码中的 Redis 和 MySQL 连接参数,以及 MySQL 表名等信息。这段代码将遍历 Redis 中的所有键,将键值对数据插入到 MySQL 的指定表中。你还可以根据实际需求进行进一步的优化和扩展。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

尹煜

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值