基于redis的位图实现签到功能
基于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 - 1
key = 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 - 1
consecutive = 0
for offset in range(max_offset, -1, -1):
if redis.getbit(key, offset):
consecutive += 1
else:
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_bits
return 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 True
except 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