在记录用户签到、登录状态等布尔型数据时,传统键值对存储会消耗大量内存。Redis 提供的 Bitmap 位图结构将每个状态压缩至 1 个 bit,支持自动扩容与高效统计。本文将解析位图底层原理,演示核心命令用法,并提供内存优化与性能评估依据。
位图核心原理与内存优势
位图(Bitmap)在 Redis 中属于 String 数据类型,本质是一个字节数组(bytes array)。Redis 字符串最大可存储 512 MB 内容,每个字节由 8 个 bit 组成。位图通过直接操作 bit 位(设置为 0 或 1)来存储数据,理论存储上限可达 2^32 个位。

以用户年度签到为例,若使用普通 String 存储,需为 365 天创建 365 个键值对;而使用位图,每天仅需 1 个 bit,一年 365 天仅占用约 46 字节。通过统计值为 1 的 bit 数量,即可快速得出签到天数。位图操作不仅大幅降低内存占用,还具备极高的读写效率。
自动扩容机制与二进制映射
Redis 位数组支持自动扩展。当设置的偏移量(offset)超出当前字符串长度时,Redis 会自动将中间未设置的位填充为 0,并扩容至目标位置。
以下演示如何通过位图操作将 key a 的值构建为字符串 "he"。首先获取字符的 8 位二进制码:
>>> bin(ord("h"))
'0b1101000'
>>> bin(ord("e"))
'0b1100101'将二进制码拼接后,从下标 0 开始编号,值为 1 的偏移量依次为 1、2、4、9、10、13、15。初始 key 的所有位均为 0,通过 SETBIT 命令设置对应偏移量:
127.0.0.1:6379> SETBIT a 1 1 (integer) 0 127.0.0.1:6379> SETBIT a 2 1 (integer) 0 127.0.0.1:6379> SETBIT a 4 1 (integer) 0 127.0.0.1:6379> GET a "h" 127.0.0.1:6379> SETBIT a 9 1 (integer) 0 127.0.0.1:6379> SETBIT a 10 1 (integer) 0 127.0.0.1:6379> SETBIT a 13 1 (integer) 0 127.0.0.1:6379> SETBIT a 15 1 (integer) 0 127.0.0.1:6379> GET a "he"

若操作后对应字节包含不可打印字符,Redis 会以十六进制形式显示。例如:
127.0.0.1:6379> SETBIT b 0 1 (integer) 0 127.0.0.1:6379> SETBIT b 1 1 (integer) 0 127.0.0.1:6379> GET b "\xc0"
核心位图命令详解
SETBIT:设置或清除指定位
用于设置或清除某一位的值,返回该位修改前的旧值。初始状态下所有位均为 0。
SETBIT key offset value
offset 为从 0 开始的偏移量。示例:
127.0.0.1:6379> SET user:1 a OK 127.0.0.1:6379> SETBIT user:1 0 1 (integer) 0 127.0.0.1:6379> GET user:1 "\xe1"
GETBIT:获取指定位的值
返回指定偏移量处的 bit 值(0 或 1)。
127.0.0.1:6379> GETBIT user:1 0 (integer) 1
若 offset 超出字符串长度或 key 不存在,命令返回 0:
redis> EXISTS bits (integer) 0 redis> GETBIT bits 100000 (integer) 0
BITCOUNT:统计值为 1 的位数
统计指定区间内值为 1 的 bit 数量。支持通过 start 和 end 限定字节范围,参数规则与 GETRANGE 一致,支持负数索引(如 -1 表示最后一个字节)。
BITCOUNT key [start end]
示例:
127.0.0.1:6379> BITCOUNT user:1 (integer) 8
典型应用场景与性能评估
位图特别适用于海量布尔状态记录,如用户签到、登录频次、在线状态标记等。通过 1 和 0 的紧凑排列,可快速统计活跃用户数或连续签到天数。
Redis 官方曾进行压力测试:模拟 1 亿 2 千 8 百万用户的系统,使用位图统计日均活跃用户数量,耗时仅约 50ms,且内存占用仅为 16 MB。这一数据充分验证了位图在大规模状态统计中的高效性与低开销特性。
在线练习工具:https://try.redis.io/
查看更多命令:https://redis.io/commands
