BitMap用法:
我们按月来统计用户签到信息,签到记录为1,未签到则记录为0.(布隆过滤器就是采用这种结构)
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-RsaZg9D6-1669736765619)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221129181234928.png)]](/uploadfile/202403/7ac8e1930957845.png)
把每一个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_RD:获取BitMap中bit数组,并以十进制形式返回
BITOP:将多个BitMap的结果做位运算(与,或,异或)
BITPOS:查找bit数组中指定范围内第一个0或1出现的位置
签到功能
需求:实现签到接口,将当前用户当天签到信息保存到Redis中
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9vsPG5i8-1669736765621)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221129212906306.png)]](/uploadfile/202403/54bef4e700d12b1.png)
提示:因为BitMap底层是基于String数据结构,因此其操作也都封装在字符串相关操作中了。
![[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Q0TxiynJ-1669736765622)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221129213015824.png)]](/uploadfile/202403/321b682035b016f.png)
代码实现
public Result sign() {//1.获取当前登录的用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyy-MM"));String key=USER_SIGN_KEY+userId+keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//5.写入Redis SETBIT key offset 1stringRedisTemplate.opsForValue().setBit(key,dayOfMonth-1,true);return Result.ok();}
统计连续签到
什么叫连续签到天数?
从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-BTQliNoL-1669736765623)(C:\Users\20745\AppData\Roaming\Typora\typora-user-images\image-20221129225245656.png)]
如何得到本月到今天为止的所有签到数据?
BITFELD key GET u[dayOfMonth]0
如何从后向前遍历每一个bit位?
与1做运算,就能得到最后一个bit位
随后右移1位,下一个bit为就成为最后一个bit为。
代码实现:
public Result signCount() {//1.获取当前登录的用户Long userId = UserHolder.getUser().getId();//2.获取日期LocalDateTime now = LocalDateTime.now();//3.拼接keyString keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyy-MM"));String key=USER_SIGN_KEY+userId+keySuffix;//4.获取今天是本月的第几天int dayOfMonth = now.getDayOfMonth();//5.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字List result = stringRedisTemplate.opsForValue().bitField(key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0));if (result == null || result.isEmpty()){//没有任何签到结果return Result.ok(0);}Long num =result.get(0);if (num == null || num==0){return Result.ok();}//6.循环遍历int count=0;while (true) {//6.1让这个数字与1做与运算,得到数字的最后一个bit位//判断这个bit是否为0if ((num & 1) == 0){//如果为0,说明未签到,结束break;}//如果不为0,说明已签到,计数器+1count++;//把数字右移一位,抛弃最后一个bit位,继续下一个bit位num=num >>1;}return Result.ok(count);}