Redis - Bitmap 类型
Bitmap
位图的本质,还就是一个 集合,属于是 Set 类型针对整数的特化版本!--- 为了节省空间!
Redis Bitmap(位图)是一种通过位(bit)来存储和操作数据的特殊数据结构,它将每个元素映射到一个二进制位上,通过位的 0/1 状态表示元素的存在与否或状态。这种结构极大地节省了内存空间,适合处理大规模的二值状态数据。
🔍 核心特性与概念
Bitmap 的设计围绕 “高效存储二值状态”,关键特性如下:
- 位级存储:每个元素用 1 个二进制位(bit)表示,1 字节(Byte)可存储 8 个元素,1MB 可存储约 800 万个元素,内存效率极高。
- 二值状态:每个位只能是 0 或 1,适合表示 “存在 / 不存在”“在线 / 离线”“已读 / 未读” 等二元属性。
- 位运算支持:提供 AND、OR、XOR 等位操作,可高效计算多个 Bitmap 之间的交集、并集等。
- 偏移量访问:通过整数偏移量(offset)定位元素,偏移量范围理论上可达 2^32-1(约 42 亿)。
📝 核心命令分类(附 C++ 示例)
以下示例基于 hiredis 客户端,需先安装依赖(sudo apt install libhiredis-dev
)。
1. 基础操作(设置与查询)
命令功能 | Redis 命令格式 | C++ 代码示例 |
---|---|---|
设置位的值 | SETBIT key offset value | 为 user_login 中偏移量为 1001 的位设置值(1 = 登录,0 = 未登录) |
获取位的值 | GETBIT key offset | 查询 user_login 中偏移量 1001 的用户是否登录 |
统计位为 1 的数量 | BITCOUNT key [start end] | 统计 user_login 中所有登录用户的数量(位为 1 的总数) |
代码示例:用户登录状态统计
#include <hiredis/hiredis.h>
#include <iostream>
using namespace std;int main() {// 1. 连接 RedisredisContext* ctx = redisConnect("127.0.0.1", 6379);if (ctx->err) {cerr << "连接失败: " << ctx->errstr << endl;return 1;}// 2. 记录用户登录状态(SETBIT user_login 1001 1:用户ID=1001登录)// 设置用户1001、1003、1005登录(偏移量=用户ID)redisReply* reply = (redisReply*)redisCommand(ctx, "SETBIT user_login 1001 1");cout << "用户1001之前的登录状态(0=未登录): " << reply->integer << endl;freeReplyObject(reply);(void)redisCommand(ctx, "SETBIT user_login 1003 1");(void)redisCommand(ctx, "SETBIT user_login 1005 1");// 3. 查询用户1003是否登录(GETBIT user_login 1003)reply = (redisReply*)redisCommand(ctx, "GETBIT user_login 1003");cout << "用户1003当前登录状态(1=登录): " << reply->integer << endl;freeReplyObject(reply);// 4. 统计总登录用户数(BITCOUNT user_login)reply = (redisReply*)redisCommand(ctx, "BITCOUNT user_login");cout << "总登录用户数: " << reply->integer << endl; // 输出 3freeReplyObject(reply);// 5. 断开连接redisFree(ctx);return 0;
}
2. 位运算操作
命令功能 | Redis 命令格式 | 说明 |
---|---|---|
位运算并存储结果 | BITOP operation destkey key [key ...] | 对多个 Bitmap 执行 AND/OR/XOR/NOT 运算,结果存入 destkey |
查找第一个为 0 或 1 的位 | BITPOS key bit [start] [end] | 从指定范围查找第一个值为 0 或 1 的位偏移量,可用于寻找未使用的 ID |
代码示例:活跃用户交集计算
// 假设 user_active_05(5日活跃)和 user_active_06(6日活跃)已存在
// 1. 计算连续两天活跃的用户(交集:AND 运算)
redisReply* reply = (redisReply*)redisCommand(ctx, "BITOP AND user_active_05_06 user_active_05 user_active_06"
);
cout << "交集计算影响的位数: " << reply->integer << endl;
freeReplyObject(reply);// 2. 统计连续活跃用户数
reply = (redisReply*)redisCommand(ctx, "BITCOUNT user_active_05_06");
cout << "连续两天活跃的用户数: " << reply->integer << endl;
freeReplyObject(reply);// 3. 查找第一个未登录的用户ID(BITPOS user_login 0)
reply = (redisReply*)redisCommand(ctx, "BITPOS user_login 0");
cout << "第一个未登录的用户ID: " << reply->integer << endl;
freeReplyObject(reply);
🎯 典型应用场景
Bitmap 适合需要 存储大量二值状态、进行高效位运算 的场景,具体包括:
用户行为统计
- 签到系统:用一个 Bitmap 记录用户每月签到状态(偏移量 = 日期,1 = 签到),通过
BITCOUNT
统计月签到天数。 - 活跃用户分析:每日用一个 Bitmap 记录活跃用户,通过
BITOP AND
计算连续 N 天活跃的用户。
权限控制
- 用 Bitmap 存储用户权限(每个位代表一种权限),通过位运算快速判断用户是否拥有某权限(如
GETBIT user_perm 5
检查是否有编号 5 的权限)。
ID 去重与状态标记
- 过滤已处理的任务 ID:用 Bitmap 标记已处理的任务(偏移量 = 任务 ID),避免重复处理。
- 黑名单判断:用 Bitmap 存储黑名单用户,快速检查某用户是否在黑名单中。
数据压缩存储
- 存储布尔数组:如某设备的传感器状态(0 = 正常,1 = 异常),相比数组存储节省大量内存。
📌 C++ 开发注意事项
- 偏移量范围:
offset
是无符号整数,最大支持 2^32-1(约 42 亿),但实际使用需考虑 Redis 内存限制。 - 初始化问题:未设置的位默认值为 0,无需预先初始化整个 Bitmap。
- 批量操作:单次
SETBIT
只能操作一个位,批量设置需多次调用,高并发场景建议用管道(pipeline)优化。 - 位运算性能:
BITOP
性能与 Bitmap 大小相关,对超大 Bitmap 运算可能阻塞 Redis,建议在非高峰时段执行。 - 内存计算:Bitmap 占用内存 = (最大偏移量 + 1) / 8 字节,例如最大偏移量为 1 亿时,约占用 12MB 内存。