P13 如何使用 Redis 记录上亿用户连续登录天数
面试题:上亿用户,要记录每个用户的连续登录天数(如"已连续签到 N 天"),Redis 怎么设计?
推荐方案:Redis Bitmap 位图
核心思路:每个用户一个 key,用 bit 位表示某一天是否登录。
text
key:login:{userId}
bit 位:日期偏移量(如距离某个基准日的天数)用户第 N 天登录,就把第 N 位设为 1:
bash
SETBIT login:10001 100 1判断某天是否登录:
bash
GETBIT login:10001 100统计一段时间内登录天数:
bash
BITCOUNT login:10001 0 -1连续登录天数:从今天开始往前数,直到遇到第一个 0 为止。
为什么用 Bitmap
- 省内存:一位只占 1 bit,一个用户一年 365 天只要约 46 字节;一亿用户一年约 4.4GB(按活跃用户粒度拆分后更少);
- 操作简单:
SETBIT/GETBIT/BITCOUNT都是 O(1); - 天然支持批量统计:
BITOP可以对多个用户做并集/交集(如统计哪些天两个用户都登录)。
设计细节
1. 位偏移怎么算
用固定基准日(epoch day):
text
offset = (当天日期 - 基准日期) 的天数比如以 2024-01-01 为第 0 天,2024-01-15 的 offset 就是 14。key 里也可以带年份防止 key 无限增长(login:{year}:{userId})。
2. 连续登录计算
text
count = 0
for offset 从今天往前:
if GETBIT(key, offset) == 1: count++
else: break今天没登录则连续天数为 0;或者用"截止到昨天"的口径(连续登录通常指历史累计连续)。
3. 内存控制
- 只给活跃用户建 key(不活跃的 0 数据不占位);
- 定期清理超长不活跃的 key;
- 超大用户量可对用户分片(按 userId 取模分桶到不同 Redis 实例)。
加分点
- 对比方案:Hash 存
{date: 1}也可以,但内存和查询效率远不如 Bitmap; - 扩展:连续签到奖励、排行榜"连续登录天数"都能基于这个结构计算;
- 结合
BITFIELD批量读写多个 bit,减少 RTT。
一句话总结
每个用户一个 Bitmap key,登录日对应 bit 置 1,用 GETBIT 逐天回推统计连续天数;省内存、O(1) 操作,是上亿用户连续登录统计的经典方案。