Redis Bitmap签到需按月分key、offset减1、用位运算数连续1;key格式为sign:{user_id}:{yyyyMM},写入查询时date('j')-1转整型,BITCOUNT统计总次数,BITFIELD结合右移与运算计算连续签到天数。

直接用 Redis Bitmap 做签到,核心就三点:key 要按月分、offset 要减 1、统计逻辑得从二进制里“数连续 1”。TP6.0 本身不封装 Bitmap 操作,靠的是底层 Redis 客户端(比如 predis 或 phpredis),关键在怎么组织命令和解析返回值。
Key 设计要带年月,避免跨月混乱
不能用一个 key 存全年或永久数据,否则 BITCOUNT 和 BITFIELD 都会越界或不准。推荐格式:sign:{user_id}:{yyyyMM},例如 sign:123:202607 表示用户 123 在 2026 年 7 月的签到记录。
- 每月自动切换 key,无需手动清理旧数据,可配合 Redis 过期策略(如
EXPIRE sign:123:202606 3600000)延长保留时间 - 避免用
yyyy单独做 key——一年 366 天刚好占满一个 Bitmap,但跨年时连续签到会断,必须按月切 - TP6 中拼接 key 示例:
$key = 'sign:' . $userId . ':' . date('Ym');
写入和查询必须 offset - 1
Redis 的 BIT 索引从 0 开始,而日期是 1~31。7 月 9 日对应 offset 是 8,不是 9。
- 签到写入:
$redis->setBit($key, (int)date('j') - 1, 1); - 查某天是否签到:
$redis->getBit($key, 8); // 返回 1 或 0 - 别漏掉类型强转,
date('j')返回字符串,PHP 可能误判为字符串偏移
连续签到天数得靠位运算解析
BITFIELD 一次取回整个月的位图(转成十进制数),但这个数本质是二进制,得用位运算从低位开始数连续的 1。
- 取本月截至今天的全部位:
$bits = $redis->bitField($key, ['GET', 'u' . date('j'), 0]);→ 返回类似[12345]的数组 - 拿到数值后右移+与运算判断最低位:
$n = $bits[0] ?? 0; $count = 0; while ($n & 1) { $count++; $n >>= 1; } - 注意:如果今天是 7 月 9 日,只取前 9 位(offset 0~8),高位无效位不影响结果
总签到次数用 BITCOUNT 最省事
统计当月签了多少天,不用自己遍历,交给 Redis 原生命令就行。
-
$total = $redis->bitCount($key);—— 默认统计整个 key,对按月 key 来说就是当月总天数 - 如果只想统计 7 月 1 日到 7 月 9 日:
$redis->bitCount($key, 0, 8);(start/end 是字节偏移,不是 bit 偏移;一般不建议手动指定,易错) - TP6 使用 redis 驱动时,确保配置了正确的连接方式(tcp 或 unix socket),否则 bitField 可能报错


















