【遇见青山】基于Redis BitMap实现签到功能案例

1.BitMap

我们按月来统计用户签到信息,签到记录为1,未签到则记录为0

把每一个bit位对应当月的每一天,形成了映射关系。用0和1标示业务状态,这种思路就称为位图(BitMap)。

Redis中是利用string类型数据结构实现BitMap,因此最大上限是512M,转换为bit则是2^32个bit位。

BitMap常用命令:


2.签到代码实现

首先,基于BitMap实现进行当日的签到:

/**
 * 签到
 *
 * @return Result
 */
@Override
public Result sign() {
          
   
    // 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    // 获取日期
    LocalDateTime now = LocalDateTime.now();
    String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix;
    // 获取今天是本月第几天
    int dayOfMonth = now.getDayOfMonth();
    // 写入bitmap,进行签到
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.ok();
}

今天是当月的第15天,则在BitMap的第15位将置为1:


3.连续签到天数

lowbit运算,判断连续签到天数:

很简单代码实现:

/**
 * 计算连续签到天数
 *
 * @return Result
 */
@Override
public Result signCount() {
          
   
    // 获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    // 获取日期
    LocalDateTime now = LocalDateTime.now();
    String keySuffix = now.format(DateTimeFormatter.ofPattern("yyyyMM"));
    String key = USER_SIGN_KEY + userId + keySuffix;
    // 获取今天是本月第几天
    int dayOfMonth = now.getDayOfMonth();

    // 获取本月到今天的签到记录
    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);
    }

    // 连续签到天数
    int count = 0;

    // 循环lowbit运算
    while ((num & 1) != 0) {
          
   
        count++;
        num >>>= 1;
    }
    return Result.ok(count);
}
经验分享 程序员 微信小程序 职场和发展