Redis Bitmap本质是String的位操作,需用SETBIT/GETBIT等原生命令,Spring Data Redis不提供高级封装;键名应含用户ID与日期,如sign:uid:123:20240501;签到用setBit设对应offset为1;批量操作推荐BITFIELD;统计用BITCOUNT并注意空key与过期设置。

Bitmap在Redis里不是独立数据类型,而是string的位操作
很多人一看到“Redis Bitmap”就以为是新结构,其实它只是对SETBIT、GETBIT、BITCOUNT这些命令的操作封装,底层还是String。Spring Data Redis提供了RedisTemplate和StringRedisTemplate两种模板,但直接用opsForValue()没法做位操作——必须用execute()配合RedisCallback或SessionCallback。
常见错误是试图用redisTemplate.opsForValue().set("sign:20240501", "1")来模拟签到,这完全没用:它存的是字符串,不是位图;后续BITCOUNT会报错或返回0。
- Bitmap操作必须走原生命令,Spring Data Redis不提供高级API封装
- 键名设计建议带用户ID和日期维度,比如
sign:uid:123:20240501,避免单个key过大 - 如果想按月统计,可用
BITOP AND合并多日key,但要注意key数量不能超1024(Redis限制)
用execute()调用SETBIT实现每日签到
签到本质就是把某一天对应的位置设为1。假设用户ID=123,今天是2024-05-01,我们约定:每月1号对应bit 0,2号对应bit 1……31号对应bit 30。那么5月1日就是bit 0。
redisTemplate.execute((RedisConnection connection) -> {
String key = "sign:uid:123:202405";
long offset = 0; // 5月1日 → offset=0
connection.setBit(key.getBytes(), offset, true);
return null;
});
注意几个关键点:
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
-
key.getBytes()必须用字节数组,不能传String;否则setBit会静默失败 -
offset是long类型,最大支持2^64-1,但实际别超过几百万,否则key体积暴涨 - 不要在循环里反复调用
execute(),批量签到要用BITFIELD(见下一条)
用BITFIELD一次完成多日签到或查询连续签到天数
BITFIELD是Redis 3.2+引入的原子命令,能在一个请求里读写多个位,比多次SETBIT/GETBIT快得多,也避免并发问题。比如要查用户最近7天是否全签到,可以这样:
List<Long> results = redisTemplate.execute((RedisConnection connection) -> {
String key = "sign:uid:123:202405".getBytes();
return connection.bitField(key,
BitFieldSubCommand.get(BitFieldSubCommand.BitFieldType.unsigned(7)).offset(0)
);
});
// results.get(0) 就是低7位组成的整数,值为127表示7天全签到
更实用的是计算「当前连续签到天数」:从今天往前扫,直到遇到第一个0。这时用BITFIELD配合GET和足够大的位宽(比如32)一次取回,再在Java里遍历判断,比发32次GETBIT强太多。
-
BITFIELD返回的是List<Long>,每个元素对应一个GET子命令的结果 - 位宽选太小(如
u1)只能读1位,没意义;选太大(如u64)可能读到未设置的高位,默认补0 - 如果跨月(比如4月30日→5月1日),必须拆成两个key查,Redis不支持跨key位运算
用BITCOUNT统计当月签到次数时,小心key过期和空key
BITCOUNT统计指定key中值为1的bit个数,很适合算当月打卡总数。但容易踩两个坑:
- 如果用户当月从未签到,key不存在,
BITCOUNT返回0——这是对的;但如果key存在但全是0(比如误操作写了0),也会返回0,无法区分“没签过”和“全签了0” - 没设过期时间的话,
sign:uid:123:202405会一直占内存;建议在创建key时用EXPIRE设32天过期(覆盖整月+缓冲) - 高并发下,多个线程同时对同一key执行
SETBIT是安全的(Redis单线程),但BITCOUNT前要不要加锁?不用——它只读,且结果实时
真正麻烦的是“首次签到自动建key+设过期”,得在execute()里用SETNX + EXPIRE组合,或者干脆用SET key value EX 2592000 NX一条命令搞定。

















