用 getbit 可查单日签到状态:key 为 sign:yyyymm:uid,offset 为当月第几天减1,返回1表示已签、0表示未签;错误做法是用时间戳或超31的 offset,会导致内存浪费。

查用户某天是否签到,直接用 GETBIT;查当月签到天数,用 BITCOUNT;但查“当前连续签到天数”不能靠单条命令,得组合逻辑或额外结构。
怎么用 GETBIT 查单日签到状态?
这是最轻量、最常用的查询方式,O(1) 时间,无误判风险。
- key 必须和写入时一致,比如
sign:202607:1001(年月+用户ID),offset 是「当月第几天减 1」:7 月 14 日 → offset = 13 - 执行
GETBIT sign:202607:1001 13,返回1表示已签,0表示未签,key 不存在时也返回0 - 常见错误:把时间戳当 offset(如
1720890900),导致位图稀疏、内存暴涨;或 offset 超出 31(7 月最多 31 天),高位全 0 但分配了大量空字节
怎么用 BITCOUNT 统计当月签到总天数?
适合做月度汇总、排行榜基础数据,注意它统计的是整个 key 的 bit 1 个数,不是按日期范围。
Redis 8.2.3 是一款安全优先的高性能键值存储系统。该版本紧急修复了可能引发远程代码执行(RCE)的高危漏洞(CVE-2025-62507),并解决了 HyperLogLog 及 Cuckoo Filter 等数据结构在特定场景下的崩溃问题。建议所有用户立即升级,以保障生产环境的系统稳定与数据安全。
- 如果 key 是按月设计的(如
sign:202607:1001),直接BITCOUNT sign:202607:1001即可得到该用户 7 月签到天数 - 若 key 是跨月或按天聚合的(如
sign:20260701存全站当日签到),则需配合[start end]参数截取字节范围——但注意:单位是字节,不是位,换算麻烦且易错 - 性能提示:
BITCOUNT时间复杂度是 O(N),N 是底层字符串字节数。一个 31 位的 bitmap 实际只占 4 字节,很快;但若因 offset 错误导致分配了 1MB 空间,哪怕只设 1 个 bit,也会慢几毫秒
为什么 BITPOS 不能直接算连续签到天数?
BITPOS 只能找「第一个」0 或 1,而连续签到需要从最新签到日往前扫描最长连续 1 序列——这在 Redis 原生命令里没有等价操作。
- 比如用户 7 月 1–3、12–14 都签到了,
BITPOS sign:202607:1001 0 -1(从末尾往前找第一个 0)会返回 offset=10(即 7 月 11 日),但这只是断点,不是连续长度 - 想算「当前连续多少天」,必须知道最近一次签到是哪天(比如 7 月 14 日),再逐位往前检查:13→12→11…直到遇到 0。纯靠客户端循环
GETBIT最坏 O(31),高并发下不可接受 - 工程解法通常是:写入时同步维护一个额外字段(如
user:1001:streak),用 Lua 脚本保证原子性;或用BITFIELD+BITFIELD_RO批量读取一段位,减少 RTT
查历史月份签到情况,key 设计和存储策略最关键
Redis 不适合长期存所有历史数据,否则内存不可控,查旧数据也慢。
- 推荐方案:只把「当前月」bitmap 放 Redis,历史月数据落库(MySQL/ClickHouse)。查历史时走 DB,查当月走 Redis —— 启动时自动预热当前月 key
- key 命名别用
sign:user_id:yyyyMM这种,容易和其它 string 混淆;统一用sign:yyyyMM:user_id,便于运维识别和 scan 过滤 - 如果真要查跨月连续性(比如 6 月 30 日 → 7 月 1 日),不能靠两个 key 拼接计算:6 月 key 里 offset=29 是 6 月 30 日,7 月 key 里 offset=0 是 7 月 1 日,但这两个 key 之间无关联,得业务层判断日期是否连续并分别查
真正难的不是查单日或总数,而是「连续天数」这种带状态依赖的查询——它逼你做权衡:要么加额外字段维护状态,要么接受客户端多轮请求,要么引入异步校验兜底。Bitmap 本身只管存和取,连续性是业务逻辑,不是位运算能一键解决的。










