首页 / Redis 入门教程 / 位图 Bitmap 与位域 Bitfield

Redis 入门教程

位图 Bitmap 与位域 Bitfield

本教程共 40 篇 · 第 11 篇 · 更新于 2026-08-02

redisbitmapbitfield位图SETBITBITCOUNT签到状态位

11. 位图 Bitmap 与位域 Bitfield

本节目标

  • 理解 Bitmap 本质是对 String 的「按位」操作,一位只占 1 bit
  • 掌握 SETBIT / GETBIT / BITCOUNT / BITPOS / BITOP
  • 掌握 BITFIELD 在一串字节里读写任意长度的整数(u8/i16 等)
  • 用「每日签到」「用户在线状态」「多字段权限位」等实战巩固
  • 了解 BITFIELDOVERFLOW 溢出策略与 BITFIELD_RO 只读变体

Bitmap 并不是 Redis 里一种独立的数据类型,而是在 String 类型之上的一组「按位(bit)」操作命令。一个 String 的 value 就是一串字节,每个字节 8 个 bit,Bitmap 让你能单独读写其中任意偏移量上的那一个 bit。因为 1 个 bit 只占 1/8 字节,用它来表示「是/否」「开/关」这种布尔状态极其省内存:记录 1 亿用户的「今日是否在线」,理论上只需约 12 MB。

11-1 基本位操作:SETBIT / GETBIT / BITCOUNT

SETBIT key offset value 把指定偏移量(从 0 开始)的位设为 0 或 1:

命令:

127.0.0.1:6379> SETBIT sign:2026-08:user1 0 1
127.0.0.1:6379> SETBIT sign:2026-08:user1 1 1
127.0.0.1:6379> SETBIT sign:2026-08:user1 3 1

输出:

(integer) 0
(integer) 0
(integer) 0

返回值 0 表示「设置前该位是 0」。offset 0 代表第 1 天、offset 1 代表第 2 天……这样就用 1 个 bit 表示「当天是否签到」。

读取某一位用 GETBIT,统计置 1 的位数(即签到了几天)用 BITCOUNT

命令:

127.0.0.1:6379> GETBIT sign:2026-08:user1 2
127.0.0.1:6379> BITCOUNT sign:2026-08:user1

输出:

(integer) 0
(integer) 3

BITCOUNT 还支持按字节范围统计:BITCOUNT key 0 0 只统计第 1 个字节(即第 1~8 天)。BITPOS key bit 则查找第一个为 0 或 1 的位的位置,例如找到「第一次未签到的那天」:

命令:

127.0.0.1:6379> BITPOS sign:2026-08:user1 0

输出:

(integer) 2

11-2 多键位运算:BITOP

BITOP 能对多个 key 的 bitmap 做按位逻辑运算(AND / OR / XOR / NOT),结果写入目标 key。典型用途:求「连续两天都签到的用户」。

先构造第二天的签到记录:

命令:

127.0.0.1:6379> SETBIT sign:2026-08-02:user1 0 1
127.0.0.1:6379> SETBIT sign:2026-08-02:user1 3 1
127.0.0.1:6379> BITOP AND both:user1 sign:2026-08:user1 sign:2026-08-02:user1
127.0.0.1:6379> BITCOUNT both:user1

输出:

(integer) 2
(integer) 2

AND 只在两位都为 1 时结果为 1,所以 both:user1 里置 1 的位就是「两天都签到」的天数(本例第 1、4 天,共 2 天)。OR 适合合并多天活跃,NOT 用于取反。

11-3 位域 Bitfield:在一个 key 里打包多个整数

Bitmap 只能存 0/1,而 BITFIELD 让你在一段字节里按指定长度和符号读写整数,比如把「年龄(u8)」「当月积分(u16)」「VIP 等级(u4)」紧凑地塞进同一个 key 的不同偏移段,省去多次 KV 访问。

BITFIELD key [GET type offset] [SET type offset value] [INCRBY type offset increment]。其中 type 形如 u8(无符号 8 位)、i16(有符号 16 位):

命令:

127.0.0.1:6379> BITFIELD user:1001 SET u8 0 28 SET u16 8 3600 SET u4 24 5

输出:

1) (integer) 0
2) (integer) 0
3) (integer) 0

这表示:从位偏移 0 起存一个 8 位无符号整数 28(年龄),从偏移 8 起存 16 位整数 3600(积分),从偏移 24 起存 4 位整数 5(VIP 等级)。读取时指定同样的类型和偏移即可:

命令:

127.0.0.1:6379> BITFIELD user:1001 GET u8 0 GET u16 8 GET u4 24

输出:

1) (integer) 28
2) (integer) 3600
3) (integer) 5

对某个段做原子自增:

命令:

127.0.0.1:6379> BITFIELD user:1001 INCRBY u16 8 100

输出:

1) (integer) 3700

积分从 3600 变成了 3700。BITFIELD 的多条子命令在同一次调用里原子执行,非常适合「一次更新多个计数/状态位」。

溢出策略由 OVERFLOW 控制:默认 WRAP(回绕,如 255+1 变 0),还可选 SAT(饱和,封顶到最大/最小值)或 FAIL(溢出时本次操作失败、返回 nil)。

提示:Redis 8.x 中 BITFIELD 增加了只读变体 BITFIELD_RO,它只能执行 GET 子命令、不会修改数据,便于在副本(replica)或需要严格只读的场景下安全使用。涉及位域的语义细节可参考官方 redis.io/docs 的 bitfields 说明。

11-4 实战场景

场景一:每日签到 / 打卡。 一个 key 用一个月 31 个 bit 记录用户是否签到,BITCOUNT 得月度签到天数,一个月每个用户仅约 4 字节。

场景二:用户在线状态 / 功能开关。 每个 bit 代表一个功能是否开启,或某天是否活跃,方便用 BITOP 做批量关系运算。

场景三:多字段权限位(RBAC)。BITFIELD 把「读/写/删/管理」等若干权限位打包,一次 GET 读全部权限,一次 SET/INCRBY 改其中一个,高效且省内存。

场景四:限流计数器。 把多个时间窗口的计数用 BITFIELD 的不同段存储,原子更新、统一读取。

11-5 常见误区

  • 误区:Bitmap 是独立类型。 它只是 String 上的位操作,key 仍是一个普通的 String,你甚至可以用 GET 看到它的原始字节。
  • 误区:offset 以「字节」为单位。 SETBIT 的 offset 是偏移(例如 8 才是第 2 个字节的第 1 位),不是字节。
  • 误区:BITFIELD 只能存正整数。 支持有符号类型 i8/i16/i32/i64,可表示负数;注意选错 u/i 会解释错误。
  • 误区:BITCOUNT 永远统计全部。 不指定范围时统计整个 key,指定 start end 是按字节范围而非位范围。

11.x Bitmap / Bitfield 的取舍与边界

什么情况下该用 Bitmap,而不是把布尔状态塞进 Set?核心看「是否只关心开/关,且数量巨大」。如果你要记录「1 亿用户今日是否在线」,用 Set 存 1 亿个 userID 要上百 MB 甚至更多,而用 Bitmap 只需约 12 MB(1 亿 bit ÷ 8)。但代价是 Bitmap 只能表达 0/1,无法附带额外信息;若每个用户还要存「在线时长、最近活跃时间」等,就该用 Hash 或 String,而不是硬塞进位。

关于底层还有两点要知道:第一,SETBIT 的 offset 是按计数的,理论上限约 2^32 - 1 个 bit(约 512 MB),所以对「按天签到」这类连续偏移的场景非常合适;若 offset 跨度极大且不连续,Bitmap 中间会产生大量全 0 的空洞字节,反而浪费空间,这时不如用 Set。第二,BITFIELD 读写整数时,不同类型(u8/i16/u32/i64 等)占用的位数不同,规划偏移时要算清各段边界,避免相邻字段互相踩踏;OVERFLOW SAT 在「计数封顶」场景(如等级不超过某上限)很实用,OVERFLOW FAIL 则适合「溢出即视为异常」的严格场景。

在性能上,Bitmap 与 Bitfield 的命令都是对单个 String 的局部操作,单次耗时极低;真正的成本出现在 BITOP 对超大 key 做全量运算,以及 BITCOUNT 扫描很长的 key 时,因此大范围统计同样建议在从库或低峰期执行。

11-6 小结

  • Bitmap = String 上的按位操作,1 bit 表示 1 个布尔量,极致省内存。
  • 命令:SETBIT / GETBIT / BITCOUNT / BITPOS / BITOP(AND/OR/XOR/NOT)。
  • BITFIELD 在一段字节内按长度读写整数,支持 GET/SET/INCRBY,溢出策略 WRAP/SAT/FAIL,只读变体 BITFIELD_RO
  • 用武之地:签到打卡、在线状态、权限位、多字段紧凑计数。

提示:关于版本——本教程正文统一以 Redis 8.x(最新稳定版)为准;官方 redis.io 下载页另标 8.8 为 “Latest stable”,而 GitHub 上 redis/redis 的最新发布 tag 为 8.10.0,二者同属 8.x,命令与类型差异对教材影响极小。