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。

知识点详解:一个用户签到,会改动哪一位?
位图不是保存一串“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} 标签演示同槽,但生产不能把所有数据不加判断地塞进一个槽;应按可聚合范围安排分片。

用户位图和个人签到日历,是两套坐标
上面的位图按天建 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 不直接当位偏移。
- 工程:同槽聚合、分片、时区、过期与大操作耗时都要考虑。
公司面试真题
这道题暂未收录可核验的公司真题来源。你可以先阅读本文解析,或浏览已收录的公司面试真题。
浏览公司面试真题 →