基于Redis位图实现签到功能是一种高效且节省内存的方法。以下是分步实现的详细方案:
1. 键设计策略
采用 sign:<userId>:<YYYYMM>
格式存储每月签到数据
# 示例:用户1001在2023年8月的签到数据
sign_key = "sign:1001:202308"
2. 核心操作实现
2.1 用户签到
# 命令格式
SETBIT key offset 1# 示例:8月3日签到(偏移量从0开始计算)
SETBIT sign:1001:202308 2 1
# Python伪代码
def sign(user_id):today = datetime.now()offset = today.day - 1 # 日期转0-based偏移量key = f"sign:{user_id}:{today.strftime('%Y%m')}"redis.setbit(key, offset, 1)
2.2 查询签到状态
# 命令格式
GETBIT key offset# 示例:查询8月3日是否签到
GETBIT sign:1001:202308 2
def check_sign(user_id, date):offset = date.day - 1key = f"sign:{user_id}:{date.strftime('%Y%m')}"return redis.getbit(key, offset)
2.3 统计当月签到次数
# 命令格式
BITCOUNT key# 示例:统计8月总签到次数
BITCOUNT sign:1001:202308
2.4 获取连续签到天数
def get_continuous_days(user_id):today = datetime.now()key = f"sign:{user_id}:{today.strftime('%Y%m')}"max_offset = today.day - 1consecutive = 0for offset in range(max_offset, -1, -1):if redis.getbit(key, offset):consecutive += 1else:break# 检查跨月情况if consecutive == today.day:last_day = today - timedelta(days=today.day)prev_key = f"sign:{user_id}:{last_day.strftime('%Y%m')}"prev_bits = redis.bitcount(prev_key)if prev_bits == last_day.day:consecutive += prev_bitsreturn consecutive
3. 高级功能扩展
3.1 签到日历生成
def get_sign_calendar(user_id, year_month):key = f"sign:{user_id}:{year_month}"value = redis.get(key) or b'\x00'# 将二进制数据转换为位列表bits = bin(int.from_bytes(value, byteorder='big'))[2:]return [bool(int(bit)) for bit in bits.zfill(32)] # 最多显示31天
3.2 月度统计报告
# 获取当月首次签到日期
BITPOS key 1# 获取当月最后签到日期
BITPOS key 1 -1
4. 性能优化步骤
- 数据分片:对活跃用户使用多个位图分段存储
- 缓存策略:对频繁访问的统计结果进行短期缓存
- 异步处理:非实时统计任务使用后台进程处理
- 数据归档:定期将历史数据转存到持久化存储
5. 异常处理机制
- 日期有效性验证:
def validate_date(year, month, day):try:datetime(year, month, day)return Trueexcept ValueError:return False
- 偏移量范围检查:
max_day = calendar.monthrange(year, month)[1]
if offset >= max_day:raise InvalidOffsetError("超出当月天数范围")
6. 数据可视化示例
生成签到日历JSON:
{"202308": {"total": 18,"continuous": 5,"calendar": [{"day": 1, "signed": true},{"day": 2, "signed": false},...]}
}
7. 内存使用估算
假设:
- 每月最大31天
- 每个用户每月占用4字节(31位)
- 10万活跃用户
总内存消耗:100,000用户 × 12月 × 4字节 ≈ 4.8MB