引言在前面的 Redis 系列中我们学习了五种基本数据类型和发布订阅。今天要讲的位图Bitmap不是一种独立的数据类型而是String 类型的一种特殊用法——把字符串当成二进制位数组来操作。位图的核心思想是用一个 bit 表示一个状态0 或 1。1 字节 8 个 bit可以表示 8 个独立的状态。这种极致的内存效率让位图特别适合海量数据的布尔统计场景——用户签到、活跃用户统计、权限判断等。第一部分位图的基本原理一、位图是什么位图本质就是一个String 字符串但操作的单位不是字节而是比特bit。Redis 提供了专门操作比特位的命令。关键概念概念说明offset位的偏移量从 0 开始从左向右计数最大值2³² - 1 约 42 亿 bit ≈ 512 MB底层类型String自动扩容二、为什么用位图场景传统做法位图做法节省1 亿用户签到1 亿个 key约 12MB 一个 key数千倍日活统计set/hash约 12MB数百倍权限 100 项hash 存储约 13 字节数十倍第二部分核心命令一、设置位SETBIT# 语法SETBIT key offset value # value 只能是 0 或 1 127.0.0.1:6379 SETBIT user:1:sign 0 1 # 第 0 天签到 (integer) 0 # 返回旧值 127.0.0.1:6379 SETBIT user:1:sign 1 1 # 第 1 天签到 (integer) 0 127.0.0.1:6379 SETBIT user:1:sign 5 1 # 第 5 天签到 (integer) 0 127.0.0.1:6379 SETBIT user:1:sign 0 0 # 取消第 0 天签到 (integer) 1 # 返回旧值为 1注意offset 可以从 0 到 2³²-1约 42 亿Redis 会自动扩容字符串以容纳更大的 offset返回值是设置前的旧值二、获取位GETBIT# 语法GETBIT key offset 127.0.0.1:6379 GETBIT user:1:sign 0 (integer) 0 127.0.0.1:6379 GETBIT user:1:sign 1 (integer) 1 127.0.0.1:6379 GETBIT user:1:sign 2 # 未设置过 (integer) 0 # 默认返回 0三、统计位数BITCOUNT# 语法BITCOUNT key [start end] # 统计有多少个 1 127.0.0.1:6379 BITCOUNT user:1:sign (integer) 2 # 有两个 1 # 统计指定字节范围注意是字节不是 bit 127.0.0.1:6379 BITCOUNT user:1:sign 0 0 # 第 0 个字节 (integer) 1BITCOUNT 的 start/end 是字节索引不是 bit 索引BITCOUNT key 0 0 → 只统计第 0 个字节bit 0~7BITCOUNT key 0 1 → 统计第 0~1 个字节bit 0~15BITCOUNT key 0 -1 → 统计所有字节四、查找位BITPOS# 语法BITPOS key bit [start] [end] # 查找第一个值为 bit 的位置 127.0.0.1:6379 BITPOS user:1:sign 1 # 第一个 1 的位置 (integer) 1 127.0.0.1:6379 BITPOS user:1:sign 0 # 第一个 0 的位置 (integer) 0 # 在指定字节范围内查找 127.0.0.1:6379 BITPOS user:1:sign 1 1 5 # 第 1~5 字节内找第一个 1五、位运算BITOP# 语法BITOP operation destkey key [key ...] # 支持AND与、OR或、XOR异或、NOT非 127.0.0.1:6379 SETBIT user:1:day1 0 1 127.0.0.1:6379 SETBIT user:1:day2 0 1 127.0.0.1:6379 SETBIT user:2:day1 0 1 # AND两天都签到的用户 127.0.0.1:6379 BITOP AND both_days user:1:day1 user:1:day2 (integer) 1 # OR任意一天签到的用户 127.0.0.1:6379 BITOP OR any_day user:1:day1 user:1:day2 (integer) 1 # XOR恰好只签了一天的用户 127.0.0.1:6379 BITOP XOR only_one user:1:day1 user:1:day2 (integer) 1位运算的应用运算场景AND连续 N 天都签到的用户ORN 天内任意一天签到的用户XOR恰好签了奇数天的用户NOT没有签到的用户第三部分实战场景一、用户签到系统# 用户 1001 在第 0 天签到 127.0.0.1:6379 SETBIT sign:2024:06:01 1001 1 (integer) 0 # 用户 1002 签到 127.0.0.1:6379 SETBIT sign:2024:06:01 1002 1 (integer) 0 # 查看用户 1001 是否签到 127.0.0.1:6379 GETBIT sign:2024:06:01 1001 (integer) 1 # 统计今日签到人数 127.0.0.1:6379 BITCOUNT sign:2024:06:01 (integer) 2 # 统计本月全勤用户6月1日~30日都签到 127.0.0.1:6379 BITOP AND full_attendance sign:2024:06:01 sign:2024:06:02 ... sign:2024:06:30 127.0.0.1:6379 BITCOUNT full_attendance二、统计在线活跃用户# 记录每个用户的活跃状态user_id 作为 offset # 2024-06-01 活跃用户 127.0.0.1:6379 SETBIT active:2024-06-01 1001 1 127.0.0.1:6379 SETBIT active:2024-06-01 1002 1 127.0.0.1:6379 SETBIT active:2024-06-01 1005 1 # 2024-06-02 活跃用户 127.0.0.1:6379 SETBIT active:2024-06-02 1001 1 127.0.0.1:6379 SETBIT active:2024-06-02 1003 1 # 两天都活跃的用户 127.0.0.1:6379 BITOP AND active_both active:2024-06-01 active:2024-06-02 127.0.0.1:6379 BITCOUNT active_both (integer) 1 # 只有用户 1001 两天都活跃三、权限管理# 100 个权限位每位代表一个权限 # bit 0: 读权限 bit 1: 写权限 bit 2: 删除权限 bit 3: 管理权限 ... # 管理员拥有所有权限前 10 位都设为 1 127.0.0.1:6379 SETBIT user:admin:permissions 0 1 127.0.0.1:6379 SETBIT user:admin:permissions 1 1 ... 127.0.0.1:6379 SETBIT user:admin:permissions 9 1 # 普通用户只有读权限 127.0.0.1:6379 SETBIT user:normal:permissions 0 1 # 检查权限 127.0.0.1:6379 GETBIT user:normal:permissions 1 (integer) 0 # 没有写权限第四部分C 语言操作位图#include stdio.h #include hiredis/hiredis.h int main() { redisContext *c redisConnect(127.0.0.1, 6379); if (c NULL || c-err) { printf(连接失败: %s\n, c-errstr); return -1; } // 设置签到 redisReply *reply redisCommand(c, SETBIT sign:2024:06:01 1001 1); printf(设置结果(旧值): %lld\n, reply-integer); freeReplyObject(reply); // 检查是否签到 reply redisCommand(c, GETBIT sign:2024:06:01 1001); printf(签到状态: %lld\n, reply-integer); freeReplyObject(reply); // 统计签到人数 reply redisCommand(c, BITCOUNT sign:2024:06:01); printf(签到人数: %lld\n, reply-integer); freeReplyObject(reply); redisFree(c); return 0; }第五部分位图的局限性局限说明解决方案offset 映射user_id 需要是连续整数维护 user_id → 整数映射表稀疏数据浪费如果 offset 很大但只有几个 1用集合或布隆过滤器不可直接遍历 1BITCOUNT 只统计数量BITPOS 逐个查找字符串最大 512MBoffset 最大约 42 亿分片存储总结一、核心命令速查命令作用SETBIT key offset 0/1设置某位的值GETBIT key offset获取某位的值BITCOUNT key [start end]统计 1 的数量字节范围BITPOS key bit [start end]查找第一个 bit 的位置BITOP AND/OR/XOR/NOT dest key1 key2位运算二、一句话记忆Redis 位图用 String 类型的每个 bit 表示一个布尔状态1 bit 存一个信息。用 SETBIT/GETBIT 操作单个位用 BITCOUNT 统计数量用 BITOP 做集合运算。适合签到、活跃用户、权限等海量布尔统计场景空间效率是普通 key-value 的数百倍。