Skip to content

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) 操作,是上亿用户连续登录统计的经典方案。

基于 VitePress 重建