上一篇文章介绍了如何通过 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);
}