Redis实现签到功能

Bitmap

Redis:使用Redis数据结构实现BitMap完成,如01010 就表示这个月(月分以key的形式存储)第2、4天签到了

Redis命令:SetBit(指定位置存入0|1)、getBit(获取指定位置的bit,下标从0开始)

BitCount(统计BitMap中值为1的数量)、Bitpos(第一个0|1出现的位置)

Bitfield(获取bit数组,以10进制形式返回)、Bitfield_ro、Bitop、

// 签到实现
@Override
public Result sign() {
    // 1.获取当前登录用户
    Long userId = UserHolder.getUser().getId();
    // 2.获取日期
    LocalDateTime now = LocalDateTime.now();
    // 3.拼接key,生成一个基于当前日期的字符串后缀
    String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
    // key的组成:签到功能的字符常量前缀、用户id、时间(年、月)
    String key = USER_SIGN_KEY + userId + keySuffix;
    // 4.获取今天是本月的第几天
    int dayOfMonth = now.getDayOfMonth();
    // 5.写入Redis SETBIT key offset 1
    stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    return Result.ok();
}

//连续签到统计。
//思路:获得当前这个月的最后一次签到数据,定义一个计数器,然后不停的向前统计,直到获得第一个非0的数字即可,每得到一个非0的数字计数器+1,直到遍历完所有的数据

@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.获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字 BITFIELD sign:5:202203 GET u14 0
    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位  // 判断这个bit位是否为0
        if ((num & 1) == 0) {
            // 如果为0,说明未签到,结束
            break;
        }else {
            // 如果不为0,说明已签到,计数器+1
            count++;
        }
        // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
        num >>>= 1;
    }
    return Result.ok(count);
}
BitMap实现缓存穿透

当用户端打来一个数据库中没有的请求,如果是相同的id我们只需要缓存空对象。但是如果是不同的id呢?

初步思想:我们可以将数据库所对应的id写入到一个list集合中,当用户过来访问的时候,我们直接去判断list中是否包含当前的要查询的数据,如果说用户要查询的id数据并不在list集合中,则直接返回。

问题:但是这个主键其实并没有那么短,而是很长的一个 主键。

解决:可以把list数据抽象成一个非常大的bitmap,我们不再使用list,而是将db中的id数据利用哈希思想,比如:

id % bitmap.size = 算出当前这个id对应应该落在bitmap的哪个索引上,然后将这个值从0变成1,然后当用户来查询数据时,此时已经没有了list,让用户用他查询的id去用相同的哈希算法, 算出来当前这个id应当落在bitmap的哪一位,然后判断这一位是0,还是1,如果是0则表明这一位上的数据一定不存在, 采用这种方式来处理,需要重点考虑一个事情,就是误差率

### 使用 Redis 实现签到功能的最佳实践 #### 1. Redis Bitmap 基础原理 Redis 的 Bitmap 是一种基于字符串的高效数据结构,它通过将字符串视为一系列二进制位来进行操作。由于每个 bit 只能取值 `0` 或者 `1`,因此可以极大地节省存储空间[^4]。 #### 2. 用户签到的核心思路 为了实现用户签到功能,可以通过用户的唯一标识(如用户 ID)作为 key,在 value 中记录每一天对应的签到状态。具体来说: - 将日期转换为时间戳或按天编号的形式。 - 利用 `SETBIT` 和 `GETBIT` 指令分别设置和获取某一天的状态。 - 统计连续签到天数时,可借助 `BITCOUNT` 来快速计算某个范围内的总签到次数。 #### 3. 关键指令解析 以下是几个常用的 Redis Bitmap 操作命令及其作用: - **SETBIT**: 设置指定位置上的比特值为 `0` 或 `1`。 - **GETBIT**: 获取指定位置上的比特值。 - **BITCOUNT**: 计算给定范围内比特值为 `1` 的数量。 - **BITPOS**: 查找第一个匹配特定值的位置,可用于判断最近一次未签到的时间点。 #### 4. 示例代码展示 以下是一个简单的 Java 实现示例: ```java import redis.clients.jedis.Jedis; public class UserCheckInService { private Jedis jedis; public UserCheckInService(String host, int port) { this.jedis = new Jedis(host, port); } /** * 用户签到方法 */ public void checkIn(long userId, long dayIndex) { String key = "checkin:" + userId; // 构造key jedis.setbit(key, dayIndex, true); // 对应dayIndex置为1 } /** * 查询用户是否已签到 */ public boolean isCheckedIn(long userId, long dayIndex) { String key = "checkin:" + userId; return Boolean.TRUE.equals(jedis.getbit(key, dayIndex)); } /** * 统计用户累计签到天数 */ public long countTotalCheckIns(long userId) { String key = "checkin:" + userId; return jedis.bitcount(key); // 返回所有bit=1的数量 } } ``` 上述代码展示了如何使用 Redis 客户端库完成基本的签到逻辑[^1]。 #### 5. 进一步优化建议 当面对大规模并发请求时,除了基础的功能外还需要注意性能调优以及高可用设计: - **批量处理**: 如果每天有大量的新用户加入,则可以在后台定期预分配一定量的空间用于容纳这些新增加的数据; - **分布式锁控制重复提交**: 防止同一时间段内多次触发相同的操作造成脏数据; - **冷热分离策略**: 对于活跃度较低的老用户资料迁移到成本更低廉但是访问速度稍慢一点的地方保存下来;对于高频次交互的新注册会员则保持在内存型数据库里头以便迅速响应需求[^2]。 #### 6. 应用场景扩展 除了普通的每日打卡之外,Bitmap 结构还非常适合用来解决其他类型的布尔属性标记问题,例如但不限于: - IP 地址池管理 – 跟踪哪些地址已经被分配出去还没有回收回来; - 商品库存跟踪–实时监控剩余货物数目变化情况并及时发出补货提醒信号等等[^3]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值