SpringCloud天机学堂:Redis BitMap 实现用户签到功能详解
文章目录
1.、Redis BitMap 概述
Redis BitMap 是一种基于字符串类型的特殊数据结构,用于高效处理大量布尔值(0或1)。它在需要处理大规模数据时特别有用,如用户活动跟踪、权限管理和大规模数据的快速计数等场景。
BitMap 的核心优势在于其空间效率和操作速度。每个比特只占用一位空间,因此在处理大规模布尔值数据时非常节省内存。例如,存储 1 亿个布尔值,BitMap 只需要约 12.5MB 的内存。
2、 BitMap 的基础操作
SETBIT
SETBIT key offset value
用于设置 key 对应的位图中某个偏移量的值。
示例:
SETBIT user_activity:2024-08-19 5 1
GETBIT
GETBIT key offset
用于获取 key 对应的位图中某个偏移量的值。
示例:
GETBIT user_activity:2024-08-19 5
BITCOUNT
BITCOUNT key [start end]
用于计算位图中被设置为 1 的比特位的数量。
示例:
BITCOUNT user_activity:2024-08-19
BITFIELD
BITFIELD key [GET type offset]
用于在一次调用中对多个位进行操作。
示例:
BITFIELD user_activity:2024-08 GET u31 0
3、实现签到功能
接口设计
签到接口设计如下:
-
请求方式:POST
-
请求路径:/sign-records
-
请求参数:无
-
返回值:
{ "signDays": 10, // 连续签到天数 "points": 14 // 今日签到获取的积分 }
实现步骤
-
生成 BitMap 的 key
格式:sign:uid:xxx:yyyyMM -
获取当前日期,计算要设置的 bit 位 offset
-
使用 SETBIT 命令设置签到
-
统计连续签到天数
- 使用 BITFIELD 获取本月至今的签到记录
- 从后向前遍历,直到遇到第一个未签到日
-
计算获得的积分
- 基础积分 1 分
- 连续签到奖励:7天 10分,14天 20分,28天 40分
-
保存积分明细(TODO)
-
封装返回结果
核心代码实现
@Override
public SignResultVO addSignRecords() {
// 1. 签到
Long userId = UserContext.getUser();
LocalDate now = LocalDate.now();
String key = RedisConstants.SIGN_RECORD_KEY_PREFIX
+ userId + now.format(DateUtils.SIGN_DATE_SUFFIX_FORMATTER);
int offset = now.getDayOfMonth() - 1;
Boolean exists = redisTemplate.opsForValue().setBit(key, offset, true);
if (BooleanUtils.isTrue(exists)) {
throw new BizIllegalException("不允许重复签到!");
}
// 2. 计算连续签到天数
int signDays = countSignDays(key, now.getDayOfMonth());
// 3. 计算签到得分
int rewardPoints = calculateRewardPoints(signDays);
// 4. 保存积分明细记录(TODO)
// 5. 封装返回
SignResultVO vo = new SignResultVO();
vo.setSignDays(signDays);
vo.setRewardPoints(rewardPoints);
return vo;
}
private int countSignDays(String key, int len) {
List<Long> result = redisTemplate.opsForValue()
.bitField(key, BitFieldSubCommands.create()
.get(BitFieldSubCommands.BitFieldType.unsigned(len)).valueAt(0));
if (CollUtils.isEmpty(result)) {
return 0;
}
int num = result.get(0).intValue();
int count = 0;
while ((num & 1) == 1) {
count++;
num >>>= 1;
}
return count;
}
4. 在 Java 中使用 BitMap
在 Java 中,可以通过 Jedis 或 Spring Data Redis 等 Redis 客户端库来使用 BitMap 功能。以下是使用 Spring Data Redis 的示例:
@Autowired
private StringRedisTemplate redisTemplate;
public void signIn(Long userId, LocalDate date) {
String key = "user:sign:" + userId + ":" + date.format(DateTimeFormatter.ofPattern("yyyyMM"));
int dayOfMonth = date.getDayOfMonth();
redisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
}
public boolean checkSignIn(Long userId, LocalDate date) {
String key = "user:sign:" + userId + ":" + date.format(DateTimeFormatter.ofPattern("yyyyMM"));
int dayOfMonth = date.getDayOfMonth();
return redisTemplate.opsForValue().getBit(key, dayOfMonth - 1);
}
5、 总结
Redis BitMap 是一种高效、紧凑的位操作工具,特别适合处理大规模布尔值数据的场景。通过简单的命令和操作,可以实现快速的存储、查询和统计功能。在实现用户签到功能时,BitMap 展现了其在空间效率和操作速度方面的优势,能够有效处理大量用户的签到记录,并支持快速的连续签到统计。
结合 Java 和 Spring Data Redis,我们可以轻松地将 BitMap 集成到应用中,满足各种复杂的业务需求,如用户签到、活跃度统计等。通过深入理解 BitMap 的工作原理和使用方法,开发者可以在处理大规模数据时选择更加高效的解决方案。