SpringBoot+Redis Bitmap实现活跃用户统计
作者:myprince003
前言
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用户统计的资料请关注脚本之家其它相关文章!