Sunday面试指南

Redis Bitmap 是什么?如何用位图统计签到和活跃用户?

下面是一段教学用的模拟面试。

🧑‍💻 面试官:统计今天哪些用户签到,可以用 Redis Bitmap 吗?

🙋‍♂️ 我:可以,每个用户占一个 bit,签到设成 1,用 BITCOUNT 统计人数。

🧑‍💻 面试官:用户 ID 是一个很大的雪花 ID,也直接当偏移吗?这会分配多少内存?

🙋‍♂️ 我:不能随手这样用,偏移范围和密度都要考虑。

🧑‍💻 面试官:那一周活跃人数,把七天的 BITCOUNT 相加就行吗?同一个人每天都来了,会算几次?

一个位图先固定一种映射:「这个偏移代表谁」。统计之前,再确认要按天计次,还是跨天去重。

面试速答(60 秒版)

Redis Bitmap 是 String 上的一组按位操作,不是独立的底层数据类型。可以用每个 bit 的 0、1 表示某个状态,SETBIT 修改,GETBIT 查询,BITCOUNT 统计设为 1 的位数。

每天一份用户位图时,偏移映射到用户编号。一天的 BITCOUNT 可以得到这套映射下的活跃人数;多天去重活跃应先对齐同一映射做 OR,再统计,而不是把每天人数相加。

它适合编号范围可控、相对密集的精确状态。大而稀疏的 ID 不能直接拿来当偏移,否则可能撑大字符串;还要考虑分片、过期与大位图操作开销。需要的是近似基数还是具体成员,也会影响是否选择 Bitmap。

位图的 key、用户偏移与按位命令

知识点详解:一个用户签到,会改动哪一位?

位图不是保存一串“0”和“1”文字

假设咱们有一批用户,内部给他们分配了连续编号 0、1、2……,每天只需要记“有没有来过”。

可以建立当天的位图,让第 7 位表示编号 7 的用户,第 12 位表示编号 12 的用户。签到以后,把对应位设成 1。

这里是一位二进制状态,不是给每个用户存一个字符串字符。Redis 使用 String 的二进制内容作为位序列,并提供相应命令。Redis Bitmap 说明介绍了这个模型。

这也是为什么先确定偏移映射很重要。Redis 知道第 7 位,却不会自己知道它对应哪位真实用户;编号与用户的关系需要由系统稳定维护。

一天内重复签到,为什么不会重复计人?

下面是在独立测试键上使用的 Redis 命令示例:

SETBIT active:{demo}:2026-10-01 7 1
SETBIT active:{demo}:2026-10-01 12 1
SETBIT active:{demo}:2026-10-01 7 1
GETBIT active:{demo}:2026-10-01 7
BITCOUNT active:{demo}:2026-10-01

同一位重复设为 1,仍然只有一个 1。所以,最后 GETBIT 为 1,BITCOUNT 为 2,而不是 3。

命令本身不依赖 TypeScript 或 Python,直接用 Redis 命令就能解释状态变化,不必为同一组操作再引入两份客户端代码。

如果需求改成“今天签到多少次”,一个 bit 就不够了。它只表达两种状态,不能自然保存重复次数,也不会告诉你每次签到时间。

一周活跃人数,为什么不能每天相加?

假设第一天编号 7、12 活跃,第二天编号 12、20 活跃。

每天人数都是 2,相加是 4。但两天至少来过一次的用户是 7、12、20,只有 3 人。

因为两天使用相同的用户偏移映射,可以对两份位图做 OR:某个位只要有一天是 1,结果就是 1;然后统计结果中的 1。

SETBIT active:{demo}:2026-10-02 12 1
SETBIT active:{demo}:2026-10-02 20 1
BITOP OR active:{demo}:two-days active:{demo}:2026-10-01 active:{demo}:2026-10-02
BITCOUNT active:{demo}:two-days

结果是 3。若要求两天都活跃,可以用 AND,此处只有编号 12 满足。

Redis Cluster 中,多键操作需要满足相同 hash slot 等要求。示例用相同的 {demo} 标签演示同槽,但生产不能把所有数据不加判断地塞进一个槽;应按可聚合范围安排分片。

跨天 OR 去重与 AND 同时活跃

用户位图和个人签到日历,是两套坐标

上面的位图按天建 key,位偏移代表用户,所以能统计“这一天来了多少人”。

如果需要某个人本月的签到日历,也可以按用户和月份建 key,偏移代表月内第几天。此时 BITCOUNT 统计的是签到天数,不是活跃用户数。

两种建模都能用 bit,但轴不一样。不能把某个用户的日历和另一个用户的日历随手 OR 以后,声称得到了所有用户人数。

先把 key 表示什么、offset 表示什么写出来,后面的命令才有明确含义。跨时间聚合还要约定日期时区,不能今天按北京时间、明天又按 UTC 切分。

每日用户位图与个人签到日历的坐标区别

一人一 bit,为什么仍可能很费内存?

字符串长度要覆盖被设置的最大偏移,不是只为设成 1 的几个位置收费。

假设只有两名活跃用户,但其中一个编号接近十亿。直接设置这个偏移,底层仍要扩展到相应范围;并不是两个人只占两个 bit。SETBIT 文档还提醒,大偏移扩展可能带来分配与阻塞成本。

常见单个 Redis String 的大小上限是 512 MiB,对应最多 2^32 个位的位置范围;这也不是建议把每份位图都扩展到上限。真实规划先看编号范围、密度、分片和操作频率。

雪花 ID 等大整数一般不能直接当偏移。可以维护合适的内部映射,或者选择更适合稀疏集合的结构。映射本身也有存储与维护成本,不能在“节省内存”的计算里忽略。

稀疏大偏移导致字符串扩展的内存成本

保留状态,还要安排过期与验证

每天的位图、临时聚合结果和长期报表,可能需要不同保留时间。BITOP 产生的结果键,也要按自己的用途安排清理,不能假设它自动继承所有源键的过期策略。

验证时,准备重复签到、跨天重复用户、最大编号、不同分片和时区边界。先用明确的小集合算出预期结果,再检查位操作;最后测量真实范围下的内存与耗时。

面试官继续追问

Bitmap 能列出所有用户吗?

能检查相应位置,但把所有设位映射回用户,需要扫描和编号关系支持。只要人数,与需要成员列表,是不同需求。

和 HyperLogLog 怎么选?

Bitmap 在合理映射下保存精确位状态;HyperLogLog 主要提供近似基数,不直接保留具体成员。看误差要求、编号范围和是否需要成员判断,不能只比一个内存数字。

Bitmap 和布隆过滤器是一回事吗?

不是。都可能使用位结构,但布隆过滤器通过哈希映射表达概率性成员判断。这里一位对应一个明确编号,不是多次哈希产生的近似判断。

面试速记卡

  • 类型:Bitmap 是 String 上的按位操作,先固定 key 与 offset 的含义。
  • 单日:同一用户重复设位不重复计人,BITCOUNT 数的是 1 的个数。
  • 跨天:相同用户映射先 OR 再计数;AND 表示同时满足。
  • 成本:长度受最大偏移影响,稀疏大 ID 不直接当位偏移。
  • 工程:同槽聚合、分片、时区、过期与大操作耗时都要考虑。

公司面试真题

这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。

浏览公司面试真题 →
简历汪永久免费在线制作简历,模板直接套用、导出无水印,永久免费、下载免费,不需要付费解锁任何功能。去写简历