Redis 命令及数据类型 -- Bitmap
摘要
- 本文介绍 Redis Bitmap 数据类型
- 本文基于
redis-7.4.7,springboot-3.5.8 - Redis官网:https://redis.io/
- Redis 命令文档:https://redis.io/docs/latest/commands/
Bitmap 核心详解
-
Redis Bitmap 并非独立数据类型,而是基于 String 类型的位操作扩展
-
String 底层是字节数组,Bitmap 就是对数组中单个 bit 做读写(bit 只有 0/1 两个值)
Bitmap 命令使用方式
-
核心围绕「位设置、位查询、位统计、位运算」四类命令,是日常使用的基础
1. 位设置:SETBIT key offset value
-
给指定 key 的第 offset 位设 0/1(offset 从 0 开始,支持超大偏移量,Redis 会自动扩容)
-
offset 从左往右递增,从左到右为 0、1、2…,至少申请 8bit 空间,不足 8bit 时,会自动扩展到 8bit 即 1byte
1 | SETBIT bitkey 1 1 # 实际的bit为 01000000 |
2. 位查询:GETBIT key offset
-
查询指定偏移量的位值,不存在的 offset 默认返回 0
1 | GETBIT bitkey 1 |
3. 位统计:BITCOUNT key [start end]
-
统计 key 中值为 1 的 bit 总数,可选按字节范围(start/end 是字节索引)筛选
1 | BITCOUNT bitkey |
4. 位运算:BITOP op destkey key1 key2...
-
对多个 Bitmap 做 与(AND)、或(OR)、异或(XOR)、非(NOT)运算,结果存入 destkey
1 | # 将key1 与 key2 做按位与运算,结果存入 destkey |
5. 位查找:BITPOS key value [start end]
-
查找第一个值为 0/1 的 bit 偏移量,快速定位目标位
1 | BITPOS bitkey 1 |
核心使用场景 + 实操举例(贴合开发实战)
-
Bitmap 的核心优势是极致省内存+高效统计(1 个字节=8 个 bit,存储 1000 万条状态仅需约 1.2MB),以下是高频场景
场景1: 用户签到/打卡(最经典场景)
• 需求:记录用户每日签到状态,查询某用户某天是否签到、统计某用户月度签到次数
• 设计:key 格式 user:sign:uid:202512(用户2025年12月签到),offset 为日期(1号=0、2号=1…31号=30),签到设 1、未签到默认 0
• 实操命令:
-
12月1号签到:
SETBIT user:sign:uid:202512 0 1 -
查询12月1号是否签到:
GETBIT user:sign:uid:202512 0(返回1=签到) -
统计12月总签到次数:
BITCOUNT user:sign:uid:202512
• 优势:1个用户1个月签到仅占 4 字节(31 bit),百万用户月度签到仅占约 3.9MB,远超数据库存储的性价比。
-
劣势:若想查询该用户本月内都哪天签到了,即要查看bitmap哪些位为1,则bitmap不支持这个命令,可以在业务端实现。如需要精确查询和聚合统计则需要同步数据到关系型数据库。
场景2: 日活/周活/月活(DAU/WAU/MAU)统计(高并发场景首选)
• 需求:统计每日访问平台的用户数,快速计算周活(7天内至少访问1次)、月活,去重统计
• 设计:按日期建 Bitmap,key 格式 active:user:20251217(当日活跃),offset 设为用户唯一ID(需确保ID是连续或可映射为数字,避免超大偏移量),用户访问则设 1
• 实操命令:
-
用户ID 10086 12月17日访问:
SETBIT active:user:20251217 10086 1 -
统计12月17日日活:
BITCOUNT active:user:20251217 -
统计12月15-17日3天内活跃的用户数:
BITOP OR active:user:20251215_17 active:user:20251215 active:user:20251216 active:user:20251217→ 再执行BITCOUNT active:user:20251215_17 -
统计12月15-17日每天都登录的用户数:
BITOP AND active:user:20251215_17 active:user:20251215 active:user:20251216 active:user:20251217→ 再执行BITCOUNT active:user:20251215_17
• 优势:百万级用户日活统计,单 Bitmap 仅占约 125KB,位运算合并统计速度毫秒级,远快于数据库 group by 去重。
-
劣势:若想查看本月内哪些用户登录过,则需要遍历 Bitmap 的所有 offset 位,效率较低。如需要精确查询和聚合统计则需要同步数据到关系型数据库。
场景3: 功能开关/状态标记(多维度轻量标记)
• 需求:给用户标记多类轻量状态(如是否开通会员、是否绑定手机、是否参与活动),无需单独存多个key
• 设计:1个key对应1个用户,key 格式 user:status:10086,不同 offset 对应不同状态(offset0=是否绑定手机、offset1=是否会员、offset2=是否参与活动),1=是、0=否
• 实操命令:
-
给用户10086绑定手机:
SETBIT user:status:10086 0 1 -
开通会员:
SETBIT user:status:10086 1 1 -
查询是否是会员:
GETBIT user:status:10086 1
• 优势:1个key承载用户N个状态,无需维护多个 String/Hash,查询和修改均为O(1),极简高效。
-
劣势:若想查看用户所有状态,则需要遍历所有 offset 的位,效率较低,另外统计哪些用户开启了某个状态也比较麻烦。如需要精确查询和聚合统计则需要同步数据到关系型数据库。
场景4: 布隆过滤器底层实现(核心依赖Bitmap)
• 需求:实现海量数据的快速去重判断(如缓存穿透防护、海量URL去重),允许极小误判率,不允许漏判
• 设计:用1个大 Bitmap 作为底层存储,配合多个哈希函数 —— 数据存入时,通过多个哈希函数算出多个 offset,将对应 bit 设为1;查询时,若所有哈希对应的 offset 都是1,则大概率存在,否则一定不存在
• 实操:Redis 7 可直接用 Bitmap 手动实现,也可结合 RedisBloom 扩展(更易用),核心原理是 Bitmap 的位设置与查询。
• 优势:存储1亿条数据,误判率5%的布隆过滤器,仅需约 12MB 内存,查询速度极致快。
-
劣势:布隆过滤器虽然有极小误判率,但不允许删除。
注意事项(避坑关键)
-
offset 不要无限制过大:虽 Redis 支持超大 offset,但过大(如超过10亿)会导致 Bitmap 占用内存骤增,需合理规划 offset 范围(如用户ID做哈希映射压缩)
-
避免单 key 过大:单个 Bitmap 建议控制在1GB内(对应约85亿 bit),过大易导致Redis持久化/迁移耗时过长
-
注意编码兼容:Bitmap 基于 String,Redis 会自动用 RAW 编码存储,无需手动设置
Bitmap 命令
-
SpringBoot 的
StringRedisTemplate.opsForValue()中 Bitmap 数据类型 的操作方法与 Redis 原生命令的对应关系如下:
注意这里一定要用
StringRedisTemplate来操作 Bitmap
写操作(位修改)
| 方法功能 | 方法 | Redis 原始命令 | 备注 |
|---|---|---|---|
| 设置指定偏移量的位 | setBit(K key, long offset, boolean value) |
SETBIT key offset value |
返回旧值(0 / 1),offset 从 0 开始 |
offset 表示 第几位(bit),不是字节
读操作(位查询)
| 方法功能 | 方法 | Redis 原始命令 | 备注 |
|---|---|---|---|
| 获取指定偏移量的位 | getBit(K key, long offset) |
GETBIT key offset |
返回 0 / 1,不会修改数据 |
Bitmap 常用但 Spring 未直接封装的命令
-
Spring Data Redis 中通常通过 RedisCallback 或 execute 调用这些命令。
| Redis 命令 | 功能 | 说明 |
|---|---|---|
BITCOUNT key [start end] |
统计 bit=1 的数量 | 常用于活跃用户统计 |
BITPOS key bit [start end] |
查找第一个 0/1 的位置 | 常用于分配位 |
BITOP AND/OR/XOR/NOT |
位运算 | 多 bitmap 计算 |
1 | package com.example.demo.bitmap; |