基于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