redis引入场景:
或许有人会说,我只需要使用mysql数据库就能完成用户签到记录的功能了呀,为什么还要使用 redis呢?用户签到一次,对应到mysql数据库中就是一条记录。 ![在这里插入图片描述](https://img-blog.csdnimg.cn/821073e32b7840b2b3f73766f7c01952.png) 这里我们有6个字段,bigint的id、bigint的user_id、year类型的year、tiny类型的month、date类型的date、tinyint类型的is_backup。对应的字节数(8 + 8 + 1 + 1 + 3 + 1)=22字节,也就是用户签到一次就需要22字节来进行存储。我们假设一天只签到一次,按照30天来进行计算,22*30=660。也就是一个人每天签到一次,一个月需要660字节来进行存储,那当我们的用户量达到百万甚至千万,一年下来,需要的空间并不是我们可以接收的。
redis中的BitMap结构
我们按月来统计用户签到信息,签到记录为1,未签到则记录为0。把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。这样我们就用极小的空间,来实现了大量数据的表示。即便每天签到,一个月最多31位,也就是最多4个字节,与660字节相比,有百倍的容量差距。
BitMap的知识大致了解
- SETBIT:向指定位置(offset)存入一个0或1
- GETBIT :获取指定位置(offset)的bit值
- BITCOUNT :统计BitMap中值为1的bit位的数量
- BITFIELD :操作(查询、修改、自增)BitMap中bit数组中的指定位置(offset)的值
实现思路
我们可以把年和月作为bitMap的key,然后保存到一个bitMap中,每次签到就到对应的位上把数字从0变成1,只要对应是1,就表明说明这一天已经签到了,反之则没有签到。
注意:因为BitMap底层是基于String数据结构(这里的String是redis中的String),因此其操作也都封装在字符串相关的操作中了。
1. 定义接口
/**
* 我们在这里使用测试工具(例如postman)进行测试
* 实现签到功能
* @return 返回值
*/
@PostMapping("/api/user/sign")
public Result sign() {
// 具体实现方法我们放到Impl中进行实现,不在controller中进行过多的代码编写
return userService.sign();
}
2. 具体实现代码
/**
* 用户签到功能
* @return 返回值
*/
@Override
public Result sign() {
// 1. 获取当前登录用户id
Long userId = UserHolder.getUser().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. 写入redis
/**
* 第一个参数:key,这没什么好说的,因为redis就是 key - value进行存储的(但是这个key是与现实中的月相关联的,也就是不同的月份key是不同的)
* 第二个参数: 这个位置是向哪个位置插入数据 这里减 1 的原因是因为redis是从0开始进行存储的,加入我们是第一天,对应到redis为下标为0
* 第三个参数: 填写 true或者false,代表 1或者0
*/
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
return Result.ok();
}
进行测试
- 发送请求(这里我进行了token验证,根据自己情况来)
- 查看redis结果
因为我是在2022年11月15日进行测试,所以key是正确的,前面的1011是用户id,202211正确。下面为什么这么多0?因为redis不是根据位来进行存储的,而是字节存储形式, 一个字节8位,所以不满8的倍数的,自动补齐0。而1是第十五个位置,测试正确!!!
关于上面的一些用法说明
- 为什么使用StringRedisTemplate而不是使用RedisTemplate。因为如果使用RedisTemplate的话,它会在我们存储对象信息的时候,会自动添加一行关于类的来源信息,因为RedisTemplate在对象与Json转换的时候不需要我们进行手动配置,但是它需要消耗我们的内存。