使用redis的BitMap使用入门;统计签到
Posted 好大的月亮
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了使用redis的BitMap使用入门;统计签到相关的知识,希望对你有一定的参考价值。
介绍
redis
有一个bitMap
数据结构,可以看成是一个二进制的数组,数组元素只有0
和1
。
ps:
**这里要注意bitcount
范围统计时,计算的是字节数,即一次性计算8
位里面,1
的个数。只有getbit
时后面跟的偏移量
才是从(bit位)
数起。
**
签到
命令行操作demo
是否签到: setbit key 第几天 是否签到
统计一段时间内的签到天数: bitcount key 开始日期 结束日期
java的Calendar自带获取今天是一年中第几天的方法
那么当天是否签到只要把值往redis
一塞就ok
了
Calendar calendar = Calendar.getInstance();
calendar.setTime(new Date());
System.out.println(calendar.get(Calendar.DAY_OF_YEAR));
LocalDateTime localDateTime = LocalDateTime.now();
System.out.println(localDateTime.getDayOfYear());
LocalDate localDate = LocalDate.now();
System.out.println(localDate.getDayOfYear());
来一波实际操作demo
demo
中是一个月的统计签到次数
import com.alibaba.fastjson.JSON;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.connection.BitFieldSubCommands;
import org.springframework.data.redis.core.RedisCallback;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.util.CollectionUtils;
import org.springframework.web.bind.annotation.*;
import javax.annotation.Resource;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
@RestController
@RequestMapping("/bitMap")
public class RedisBitMapController
private final String userId="155852412069";
@Resource
private RedisTemplate<String, String> redisTemplate;
/**
* 获取 当月签到总次数
* @param
* @return
*/
@GetMapping("/getKey")
public String getRedis()
String s1 = buildSignKey(userId, LocalDate.now());
Object s = redisTemplate.execute((RedisCallback<Long>) con -> con.bitCount(s1.getBytes()));
return String.valueOf(s);
/**
* 模拟签到
* @param signDay yyyy-MM-dd
* @return
*/
@GetMapping("/sign")
public String setBitFild(@RequestParam String signDay)
LocalDate localDate = formatDay(signDay);
redisTemplate.opsForValue().setBit(buildSignKey(userId, localDate), localDate.getDayOfMonth(), true);
return localDate.getDayOfMonth() + "日签到成功";
/**
* 模拟取消签到 用来测试数据用
* @param signDay yyyy-MM-dd
* @return
*/
@GetMapping("/unsign")
public String unsetBitFild(@RequestParam String signDay)
LocalDate localDate = formatDay(signDay);
redisTemplate.opsForValue().setBit(buildSignKey(userId, localDate), localDate.getDayOfMonth(), false);
return localDate.getDayOfMonth() + "日取消签到成功";
/**
* 获取一个当前月份里面 截止到到传入的日期的连续签到的天数
* @param signDay
* @return
*/
@GetMapping("/getSign")
public long getBitFild(@RequestParam String signDay)
LocalDate localDate = formatDay(signDay);
long continueSignCountDay = getContinueSignCountDay(userId, localDate, 1);
System.out.println(continueSignCountDay);
return continueSignCountDay;
/**
* 检测某一天是否签到
* @param signDay
* @return
*/
@GetMapping("/checkSign")
public Boolean checkSign(@RequestParam String signDay)
LocalDate localDate = formatDay(signDay);
Boolean bit = redisTemplate.opsForValue().getBit(buildSignKey(userId, localDate), localDate.getDayOfMonth());
return bit;
public LocalDate formatDay(String signDay)
DateTimeFormatter format = DateTimeFormatter.ofPattern("yyyy-MM-dd");
return LocalDate.parse(signDay, format);
/**
* 获取一个月签到的详情
* @return
*/
@GetMapping("/getsignDetail")
public String setBitFild1()
Map<String, Boolean> signInfo = getSignInfo(userId, LocalDate.now());
System.out.println(signInfo);
return JSON.toJSONString(signInfo);
/**
* 获取一个月的总签到次数
* @return
*/
@GetMapping("getSignSum")
public String getSignSum(@RequestParam("signDay") String signDay)
LocalDate localDate = formatDay(signDay);
String signKey = buildSignKey(userId, localDate);
Long signSum = redisTemplate.execute((RedisCallback<Long>) con ->
return con.bitCount(signKey.getBytes());
);
return signSum.toString();
/**
* 获取当月连续签到最大的天数
* @return
*/
@GetMapping("/getContinueSigns")
public String getContinueSigns(@RequestParam String signDay)
LocalDate localDate = formatDay(signDay);
List<Map<String, Boolean>> signInfo = getContinueSigns(userId, localDate);
System.out.println(signInfo);
return JSON.toJSONString(signInfo);
/**
* 使用bitfield来判断用户连续签到多少天
*
* @param signKey
* @param limit
* @param offset
* @return
*/
public List<Long> bitfiled(String signKey, int limit, int offset)
List<Long> execute = (List<Long>) redisTemplate.execute((RedisCallback<List<Long>>) con -> con.bitField(
signKey.getBytes(), BitFieldSubCommands.create().
get(BitFieldSubCommands.BitFieldType.unsigned(limit)).valueAt(offset)));
return execute;
private static String buildSignKey(String uid, LocalDate date)
return String.format("u:sign:%s:%s", uid, formatDate(date, "yyyyMM"));
private static String formatDate(LocalDate date, String pattern)
return date.format(DateTimeFormatter.ofPattern(pattern));
/**
* 获取该用户的当前自然月的签到总数
*
* @param userId
* @param localDate
* @return
*/
public long getContinueSignCountDay(String userId, LocalDate localDate, int offset)
long signDayCount = 0;
List<Long> list = bitfiled(buildSignKey(userId, localDate), localDate.getDayOfMonth(), offset);
System.out.println(list.get(0));
if (!CollectionUtils.isEmpty(list) && list.size() > 0)
long result = list.get(0) == null ? 0 : list.get(0);
for (int i = 0; i < localDate.getDayOfMonth(); i++)
//相等的代表没有签到,左右右移后最右边的1会变成0
if (result >> 1 << 1 == result)
if (i > 0)
//当前日期的第一天就没签到,连续签到中断
break;
else
signDayCount += 1;
//将右移两位后的值赋值给result
result >>= 1;
return signDayCount;
/**
* 月签详情 查看哪天签到 哪天未签到
*
* @param userId
* @param date
* @return
*/
public Map<String, Boolean> getSignInfo(String userId, LocalDate date)
Map<String, Boolean> signMap = new LinkedHashMap<>(date.getDayOfMonth());
List<Long> list = bitfiled(buildSignKey(userId, date), date.lengthOfMonth(), 1);
System.out.println(list.get(0));
if (!CollectionUtils.isEmpty(list) && list.size() > 0)
// 由低位到高位,为0表示未签,为1表示已签
long result = list.get(0) == null ? 0 : list.get(0);
for (int i = date.lengthOfMonth(); i > 0; i--)
LocalDate d = date.withDayOfMonth(i);
//在redis中数据存放是从左往右的,即最右边是最近的签到。
//位移的时候是从最右边开始,所以这里从最新的日期开始
signMap.put(formatDate(d, "yyyy-MM-dd"), result >> 1 << 1 != result);
result >>= 1;
return signMap;
public List<Map<String, Boolean>> getContinueSigns(String userId, LocalDate date)
List<Long> list = bitfiled(buildSignKey(userId, date), date.lengthOfMonth(), 1);
System.out.println(list.get(0));
List<Map<String, Boolean>> maxContinueDays = new ArrayList<>();
if (!CollectionUtils.isEmpty(list) && list.size() > 0)
// 由低位到高位,为0表示未签,为1表示已签
long result = list.get(0) == null ? 0 : list.get(0);
List<Map<String, Boolean>> tmpMaxContinueDays = new ArrayList<>();
for (int i = date.lengthOfMonth(); i > 0; i--)
//在redis中数据存放是从左往右的,即最右边是最近的签到。
//位移的时候是从最右边开始,所以这里从最新的日期开始
LocalDate currentLocalDate = date.withDayOfMonth(i);
if(result >> 1 << 1 == result)
if(maxContinueDays.size() < tmpMaxContinueDays.size())
maxContinueDays.clear();
maxContinueDays.addAll(tmpMaxContinueDays);
tmpMaxContinueDays.clear();
else
//因为是倒着找过来的,所以这里连续的签到日期是最后一个最大连续签到日期
//如果想要最早的,只需要把上面maxContinueDays.size() < tmpMaxContinueDays.size()改成 <= 即可
String currentDayStr = formatDate(currentLocalDate, "yyyy-MM-dd");
Boolean isSign = result >> 1 << 1 != result;
Map<String, Boolean> tmpMap = new HashMap<>();
tmpMap.put(currentDayStr, isSign);
tmpMaxContinueDays.add(tmpMap);
result >>= 1;
return maxContinueDays;
以上是关于使用redis的BitMap使用入门;统计签到的主要内容,如果未能解决你的问题,请参考以下文章
微服务 Spring Boot 整合 Redis BitMap 实现 签到与统计
328请教一个Redis bitmap做签到功能的问题。 统计近7天连续签到用户数,bitop+bitcount是怎么用的?
Redis进阶学习05---Feed流,GEO地理坐标的应用,bitmap的应用,HyperLogLog实现UV统计