在"亿级用户、每日签到、UV 统计、防穿透"等场景下,传统 KV 存储的每用户一 key 会耗尽内存。**位图(Bitmap)与布隆过滤器(Bloom Filter)**把每个元素压缩到 1 个 bit 或几个 bit,是 Redis 内存优化最重要的两种武器。本章剖析原理、命令、内存公式与误判率工程权衡。
位图是 string 的 bit 级视图:一个 string 可以看作 bit 数组,SETBIT 把第 N 位设为 0/1。8 个 bit 占 1 字节,1 亿个用户只需 12.5MB 内存。
> setbit sign:2026-08-01 10001 1 # 用户 10001 签到
(integer) 0
> getbit sign:2026-08-01 10001 # 查询是否签到
(integer) 1
> bitcount sign:2026-08-01 # 当日签到人数
(integer) 1
> bitcount sign:2026-08-01 0 1000 # 字节区间 [0,1000](前 8008 个用户位)内的签到数;7.0 起可加 BIT 参数按 bit 计BITOP 支持 AND/OR/XOR/NOT,可用于"连续签到"统计:
# 今天与昨天都签到的人(连续签到)
> bitop and sign:both sign:2026-08-01 sign:2026-07-31
(integer) 1251 # 返回目标 key 的字节长度(= 最长源串,此处由偏移 10001 决定)
> bitcount sign:both
(integer) 1 # 两天都签到的用户数SETBIT key 4294967295 1 会立即分配 512MB,谨慎操作。| 场景 | 实现 |
|---|---|
| 每日签到 | 日期为 key,用户 ID 为偏移 |
| 在线状态 | SETBIT online 10001 1 + 心跳过期 |
| 布隆过滤器的底层 | 见下节 |
| 用户行为统计 | BITCOUNT/BITPOS 快速统计 |
缓存穿透防护、URL 去重、邮箱黑名单等场景,需要判断"元素是否存在"。精确方案(set)在数据量极大时内存爆炸;布隆过滤器用多 bit + 多次哈希换取空间,代价是误判率(可能把不存在的判成存在,反之不行)。
给定元素数 n、位数组长度 m、哈希函数个数 k:
[ p = \left(1 - e^{-\frac{kn}{m}}\right)^k ]
最优哈希函数个数:
[ k = \frac{m}{n} \ln 2 \approx 0.693 \frac{m}{n} ]
工程速查表(n=1000 万):
| 误判率 p | 位数组 m | 每元素 bit 数 | k | 内存 |
|---|---|---|---|---|
| 1% | 约 9.6 倍 n | 9.6 | 7 | 约 12MB |
| 0.1% | 约 14.4 倍 n | 14.4 | 10 | 约 18MB |
| 5% | 约 6.2 倍 n | 6.2 | 4 | 约 7.8MB |
落地方式有三种(Redis 8.0.2 起官方开源版已原生内置 BF.* 等概率结构,无需单独装模块;本机构建可用 MODULE LIST 确认是否编译):
方式一:BF 命令(Redis 8.0.2+ 内置;早期版本加载 RedisBloom 模块或使用 Redis Stack)
> bf.reserve blacklist 0.01 1000000 # 误判率 1%,预计 100 万元素
OK
> bf.add blacklist "spam@example.com"
(integer) 1
> bf.exists blacklist "spam@example.com"
(integer) 1 # 1 = 可能存在;0 = 一定不存在
> bf.madd blacklist "a@x.com" "b@x.com"
> bf.mexists blacklist "a@x.com" "c@x.com"方式二:自实现(SETBIT + 多个哈希)
用 Lua 脚本实现 k 个哈希(如 crc32 + md5 组合),写入同一个 string key 的位图。适合不想引入模块的场景,注意哈希函数独立性与位偏移计算。
方式三:客户端实现(如 Guava BloomFilter)
海量数据在客户端构建后分段写入,或直接作为 JVM 本地过滤器 + Redis 兜底。
要点:
| 维度 | 位图 | 布隆过滤器 |
|---|---|---|
| 最小单元 | 1 bit/元素(整数偏移) | 约 6-15 bit/元素 |
| 误判 | 无 | 有(可配置) |
| 删除 | 支持(覆写 bit) | 不支持 |
| 适用数据 | 整数 ID(连续/大偏移均可) | 任意字符串(URL、邮箱、手机号) |
| 典型场景 | 签到、在线、UV 去重 | 穿透防护、黑名单、爬虫去重 |
下一章介绍 HyperLogLog:用 12KB 统计亿级 UV 的基数算法。