提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
文章目录
项目背景
现要求实现一个签到功能,要求如下:
- 能够判断用户是否已经进行签到
- 能够判断用户连续签到的天数
1、理论讲解
Bitmap是一个存储二进制数字的数组,数组中每个元素的下标被称为offset。
Bitmap 不是Redis中的实际数据类型,而是在String类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全的块,且最大长度为512MB,它们适合用于设置最多 232个不同的位。
| 命令 | 介绍 |
|---|---|
| SETBIT key offset value | 设置指定 offset 位置的值 |
| GETBIT key offset | 获取指定 offset 位置的值 |
| BITCOUNT key start end | 获取 start 和 end 之间值为 1 的元素个数 |
| BITOP operation destkey key1 key2 ... | 对一个或多个 Bitmap 进行运算,可用运算符有 AND, OR, XOR 以及 NOT |
2、实现方式
用户签到功能如下所示:
- key的设计:"sign:" + userId + 日期(格式为yyyyMM)
- 写入Redis中(比如当月第5天,就将Bitmap对应的第4个位置设置为1)
java
@Override
public void sign() {
// 1.获取当前登录用户
Long userId = AuthContextUtil.getUserInfo().getId();
System.out.println(userId);
// 2. 获取日期
LocalDateTime now = LocalDateTime.now();
// 3. 拼接key
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = "sign:" + userId + keySuffix;
// 4. 获取今天是本月的第几天
int dayOfMonth = now.getDayOfMonth();
// 5. 写入Redis SETBIT key offset 1
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
}
统计今天截止的连续签到次数:
- key的设计:"sign:" + userId + 日期(格式为yyyyMM)相同业务下格式保持一致
- 获取本月为止的所有签到记录,返回的是一个十进制的数字
- 该数字的二进制形式的最低位代表的是今天签到的情况,最高位代表着是当月第一天的签到情况。
- 获取当月至今为止的签到情况,只需要从低位到高位以此与1做位运算即可,直到为0,即为断签。
java
public int signCount() {
// 1.获取当前登录用户
Long userId = AuthContextUtil.getUserInfo().getId();
// 2. 获取日期
LocalDateTime now = LocalDateTime.now();
// 3. 拼接key
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = "sign:" + userId + keySuffix;
// 4. 获取今天是本月的第几天
int dayOfMonth = now.getDayOfMonth();
// 5. 获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字
List<Long> result = stringRedisTemplate.opsForValue().bitField(
key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
);
if(result == null || result.isEmpty()){
// 没有任何签到结果
return 0;
}
Long num = result.get(0);
if(num == null || num == 0){
return 0;
}
int count = 0;
// 6. 循环遍历
// 如果为0,说明未签到,结束
// 如果不为0,说明已签到,计数器+1
while ((num & 1) != 0) {
// 6.1. 让这个数字与1做与运算,得到数字的最后一个bit位,判断这个bit位是否为0
count++;
num >>>= 1;
// 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
}
return count;
}
总结
BitMap因其结构的特殊性,所以十分适合做用户签到的功能。