1.BitMap
1.1.什么是BitMap
Redis中的Bitmap是一种特殊的数据结构,它使用一系列位来表示一组对象的状态或者标记。
1.2.BitMap的特点
- 节省空间:Bitmap使用了非常紧凑的表示方式,通常情况下比使用字符串列表等其他方式节省空间。
- 高效的位操作:Bitmap支持高效的位级别操作,如设置、清除、统计位的数量等,这使得它非常适3合于处理大规模的标记或者状态信息。
- 适合于稀疏数据:即使Bitmap的位数非常大,但只有被设置为1的位会占用内存,所以即使数据稀疏,它也能够以较小的内存开销存储。
- 原子操作:Redis提供了一系列原子操作来操作Bitmap,这意味着多个客户端可以同时对同一个Bitmap进行操作而不会出现竞态条件。
1.2.BitMap用法
1.3.BitMap的应用场景
- 标记用户在线状态:可以使用Bitmap来表示用户是否在线。例如,可以使用用户ID作为位的索引,将在线的用户的位设置为1,离线的用户的位设置为0。
- 统计用户活跃度:Bitmap可以用来统计用户的活跃度,比如每天登录的用户数、每小时访问网站的用户数等。
- 处理布隆过滤器:Bitmap可以作为布隆过滤器的底层实现,用来快速判断某个元素是否存在于集合中。
- 记录用户行为:可以使用Bitmap来记录用户的行为,比如记录用户是否点击过某个广告、是否阅读过某篇文章等。
- 处理大规模数据的集合运算:Bitmap支持位级别的运算,可以进行交集、并集、差集等操作,因此可以用来处理大规模数据的集合运算,比如用户之间的共同兴趣、共同关注的人等
2.用户签到
2.1.正常思路
2.2.比较好的做法
2.3.案例功能
@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.获取今天是本月的第几天
int dayOfMonth = now.getDayOfMonth();
// 5.写入Redis
stringRedisTemplate.opsForValue().setBit(key, dayOfMonth-1,true);
return Result.ok();
}
2.4.签到统计
从最后一次签到开始向前统计,直到遇到第一次未签到为止,计算总的签到次数,就是连续签到天数。
@Override
public Result signCount() {
// 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.获取今天是本月的第几天
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 Result.ok(0);
}
Long num = result.get(0);
if(num==null || num==0){
//没有任何签到结果
return Result.ok(0);
}
// 6.循环遍历
int count = 0;
while (true){
// 6.1.让这个数字与1做与运算,得到数字的最后一个bit位
// 6.2.判断这个bit位是否为0
if((num & 1)==0){
// 6.3.如果为0,说明未签到,结束
break;
}else{
// 6.4.如果不为0,说明已签到,计数器+1
count++;
// 6.5.数字右移一位,抛弃最后一个bit
}
num >>>= 1;
}
return Result.ok(count);
}