news 2026/5/31 7:16:28

Redis位图实战:海量数据高效处理

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
Redis位图实战:海量数据高效处理

引言

在前面的 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) 1

BITCOUNT 的 start/end 是字节索引,不是 bit 索引

BITCOUNT key 0 0 → 只统计第 0 个字节(bit 0~7)
BITCOUNT key 0 1 → 统计第 0~1 个字节(bit 0~15)
BITCOUNT 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 的数百倍。

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/5/31 7:14:28

ChatGPT多模态升级与垂直AI工具Migned的竞合分析

1. 项目概述&#xff1a;当通用AI巨头遇上垂直领域新秀最近OpenAI给ChatGPT又上了一波“硬菜”&#xff0c;什么多模态文件上传、数据分析、图像理解&#xff0c;功能堆得是越来越满。作为一个常年混迹在科技和创业圈的老兵&#xff0c;我一边感叹这AI进化速度真是“不讲武德”…

作者头像 李华
网站建设 2026/5/31 7:13:45

S1.2损失厌恶与用户忠诚度的关系:让用户觉得离开是一种损失

损失厌恶与用户忠诚度的关系&#xff1a;让用户觉得离开是一种损失导读&#xff1a;人们害怕失去的强度&#xff0c;是渴望获得的两倍。一个经典的行为经济学实验 假设你正在参加一个实验&#xff1a; 场景A&#xff1a;你得到了100元&#xff0c;然后可以选择&#xff1a; 确定…

作者头像 李华
网站建设 2026/5/31 7:13:11

鸿蒙开发-想控制光线追踪的范围?RT可见掩码怎么用

在 3D 游戏里&#xff0c;阴影和环境光遮蔽&#xff08;Ambient Occlusion&#xff0c;AO&#xff09;是让画面看起来"立体"的两个关键因素。没有阴影&#xff0c;物体会显得"飘"在地面上&#xff1b;没有 AO&#xff0c;角落和缝隙会显得太亮、不自然。 X…

作者头像 李华
网站建设 2026/5/31 7:13:07

鸿蒙开发-想让手机知道自己在哪?AR Engine的SLAM运动跟踪

你有没有想过一个问题&#xff1a;AR 应用是怎么知道手机在现实世界里移动了多少、转了多少度的&#xff1f; 比如说你在玩一个 AR 游戏&#xff0c;把一个虚拟小人放在桌面上&#xff0c;然后你拿着手机绕桌子走一圈。小人始终稳稳地站在那个位置&#xff0c;不会乱跑。这背后…

作者头像 李华