12.1 BitMap功能演示
- 我们针对签到功能完全可以通过MySQL来完成,例如下面这张表
Field |
Type |
Collation |
Null |
Key |
Default |
Extra |
Comment |
id |
bigint unsigned |
(NULL) |
NO |
PRI |
(NULL) |
auto_increment |
主键 |
user_id |
bigint unsigned |
(NULL) |
NO |
|
(NULL) |
|
用户id |
year |
year |
(NULL) |
NO |
|
(NULL) |
|
签到的年 |
month |
tinyint |
(NULL) |
NO |
|
(NULL) |
|
签到的月 |
date |
date |
(NULL) |
NO |
|
(NULL) |
|
签到的日期 |
is_backup |
tinyint unsigned |
(NULL) |
YES |
|
(NULL) |
|
是否补签 |
- 用户签到一次,就是一条记录,假如有1000W用户,平均没人每年签到10次,那这张表一年的数据量就有1亿条
- 那有没有方法能简化一点呢?我们可以使用二进制位来记录每个月的签到情况,签到记录为1,未签到记录为0
- 把每一个bit位对应当月的每一天,形成映射关系,用0和1标识业务状态,这种思路就成为位图(BitMap)。这样我们就能用极小的空间,来实现大量数据的表示
- Redis中是利用String类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是2^32个bit位
- BitMap的操作命令有
- SETBIT:向指定位置(offset)存入一个0或1
- GETBIT:获取指定位置(offset)的bit值
- BITCOUNT:统计BitMap中值为1的bit位的数量
- BITFIELD:操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
- BITFIELD_RO:获取BitMap中bit数组,并以十进制形式返回
- BITOP:将多个BitMap的结果做位运算(与、或、异或)
- BITPOS:查找bit数组中指定范围内第一个0或1出现的位置
12.2 实现签到功能
- 需求:实现签到接口,将当前用户当天签到信息保存到Redis中
|
说明 |
请求方式 |
Post |
请求路径 |
/user/sign |
请求参数 |
无 |
返回值 |
无 |
- 思路:我们可以把年和月作为BitMap的key,然后保存到一个BitMap中,每次签到就把对应位上的0变成1,只要是1就说明这一天已经签到了,反之则没有签到
- 由于BitMap底层是基于String数据结构,因此其操作也都封装在字符串相关操作中了
- 在UserController中编写对应的方法
@PostMapping("/sign")
public Result sign(){
return userService.sign();
}
@Override
public Result sign() {
//1. 获取当前用户
Long userId = UserHolder.getUser().getId();
//2. 获取日期
LocalDateTime now = LocalDateTime.now();
//3. 拼接key
String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
String key = USER_SIGN_KEY + userId + keySuffix;
//4. 获取今天是当月第几天(1~31)
int dayOfMonth = now.getDayOfMonth();
//5. 写入Redis BITSET key offset 1
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
return Result.ok();
}
- 使用PostMan发送请求测试,注意请求头中需携带登录用户的token,否则无效(又浪费我五分钟找这个问题)
- 发送成功之后,在Redis图形化界面中是可以看到的
12.3 签到统计