(三十)Redis 扩展模块 -- RedisRoaring 的安装方法
·
摘要
- 本文介绍 Redis 扩展模块 – RedisRoaring 的安装方法
- 本文基于
redis-7.4.7,springboot-3.5.8 - 操作系统:
Amazon Linux 2023(内核 6.1) - Redis官网:https://redis.io/
- Redis 命令文档:https://redis.io/docs/latest/commands/
RedisRoaring 简介
- RedisRoaring是一个开源的 Redis 模块(Redis Module),由 Antonio Guilherme Ferreira Viggiano 维护,目的是将 Roaring Bitmap 数据结构原生集成到 Redis 中作为新的数据类型与命令集。
- 它基于 CRoaring 库实现压缩位图(Roaring Bitmap)功能,这是一种高效、压缩的位图数据结构,适合处理大规模整数集合。
- 兼容 Redis 6.0 及以上版本,在不同版本中对命令元数据、ACL 分类等功能有自动适配支持。
原生的redis bitmap有哪些问题?
- 结论先行:Redis 原生 Bitmap 的核心问题是「空间不可控 + 大规模位运算性能随最大偏移线性增长」,而 redis-roaring 通过 Roaring Bitmap 解决了稀疏数据内存浪费和大范围扫描效率问题。
- Bitmap 是连续数组,不支持稀疏压缩, 其必须 从 0 到最大 bit offset 全部连续分配内存。你只要设置了 bit=1 的最大下标是 N,那么 Redis 至少要分配 N/8 字节内存。
- 假设你要存储用户ID集合,但是存储的ID范围较大,比如 1-2000000000,那么就会导致Bitmap的bit offset非常大,导致Bitmap的存储空间非常大。
- 另外如果数据比较稀疏,极端情况下只存储了2个ID,ID1:1,ID2:2000000000,那么Bitmap就要按照最大的 offset进行分配空间,导致Bitmap的存储空间非常大,但是中间的空间又被浪费了。
SETBIT user:bitmap 1 1
SETBIT user:bitmap 2000000000 1
# 2_000_000_000 bits ≈ 250 MB
# Redis 内部必须扫描:0 ... max_offset,即使只有 2 个 bit 为 1,也必须扫描 250MB 内存。
BITCOUNT user:bitmap
Roaring Bitmap实现思路
- 将32位的int类型数据进行
高16位、低16位划分,高16位就被划分成2^16=65536个大桶(容器),低16位最多被划分成2^16=65536个小桶(元素) - 把要统计的数字拆分位高16位和低16位,高16位用作容器的索引、用于定位数字在哪个容器;低16位用作容器内元素的索引、用作定位数字在容器内的位置。
- 高16位和低16位的计算
公式:x = high * 2^16 + low
对一个整数 x:
高 16 位(High) = x / 2^16(整数除法)
低 16 位(Low) = x % 2^16
高 16 位用于定位“容器(container)”,低 16 位用于定位容器内部的位置。
假设一个 32 位整数:x = 305419896 = 0x12345678 (十六进制)
高 16 位(High) = x / 2^16 = 305419896 / 65536 = 4660
低 16 位(Low) = x % 2^16 = 305419896 % 65536 = 22136
属于:container 4660, offset 22136
转为二进制(32 位)进行验证:
0001 0010 0011 0100 | 0101 0110 0111 1000
↑ 高 16 位 | ↑ 低 16 位
| 部分 | 二进制 | 十六进制 | 十进制 |
| ---- | ------------------- | ------ | ----- |
| 高16位 | 0001 0010 0011 0100 | 0x1234 | 4660 |
| 低16位 | 0101 0110 0111 1000 | 0x5678 | 22136 |
边界例子(容器切换点)
x = 65535
high = 65535 / 65536 = 0
low = 65535 % 65536 = 65535
属于:container 0, offset 65535 (容器0的最后一个位置)
x = 65536
high = 65536 / 65536 = 1
low = 65536 % 65536 = 0
属于:container 1, offset 0 (容器1的第一个位置)
- 容器有三种不同类型:
ArrayContainer、BitmapContainer和RunContainer,根据要统计的数字的数量和数字的连续性自动选择合适的容器。
| 类型 | 说明 |
|---|---|
| ArrayContainer | 稀疏(少量整数),元素为short类型的有序数组 最多存储 65536 个 short 元素,占用 128 KB 内存 |
| BitmapContainer | 密集 ,使用bitmap存储数据,固定占用 8kB 内存 |
| RunContainer | 连续区间,使用Run-Length Encoding方式压缩存储的元素,对连续数据的压缩效果特别好。 它的原理是,对于连续出现的数字,只记录初始数字和后续数量。即: 对于数据集:5,会压缩为5, 0 对于数据集:5, 6, 7, 8, 9, 10,会压缩为5, 5 对于数据集:5, 7, 9, 11, 12,会压缩为5, 0, 7, 0, 9, 0, 11, 1 最好情况:全部是连续数字,只会存储 2 个 short ,占用 4 bytes 最坏情况:0~65535的范围内填充所有的奇数位或偶数位,需要 65536 个short,占用 128 KB |
容器的使用及容器之间的转换
- Roaring 的目标是:在保证操作性能的前提下,使内存占用最小。
- 因此:插入、删除、合并、运算后,会评估当前 container 的“最优类型”,必要时自动转换
- ✅ Array → Bitmap,触发条件:容器中元素数量 > 4096
原因:
Array 每个元素占 2 bytes
4096 × 2 = 8192 bytes
Bitmap 固定 8192 bytes
超过这个点,Bitmap 更省内存 + 查询更快。
- ✅ Bitmap → Array,触发条件:容器中元素数量 <= 4096,一般在大量删除或 AND 运算后触发。
- ✅ RunContainer ↔ Array / Bitmap,Run 没有固定阈值,而是 动态评估内存模型,基于如下三个指标:
- run_count : 当前 RunContainer 中有多少个连续区间
例如集合:{1,2,3,4,5, 10,11,12, 100} 连续区间:[1..5], [10..12], [100..100] 因此:run_count = 3
| run_count | 数据形态 | 适合 Run 吗 |
|---|---|---|
| 很小(1~几十) | 大段连续 | 极其适合 |
| 中等(几百) | 稍碎 | 可能适合 |
| 很大(上千) | 高度碎片化 | 不适合 |
- run_total_coverage : run 覆盖的总元素数量(本质就是元素的数量)
[1..5] → 5 个
[10..12] → 3 个
[100..100] → 1 个
run_total_coverage = 9
实际上更重要的指标是 平均 run 长度
平均 run 长度 = run_total_coverage / run_count = 9 / 3 = 3
| run_count | coverage | 平均长度 | 形态 | 适合 Run 吗 |
|---|---|---|---|---|
| 1 | 60,000 | 60,000 | 超长连续 | 极适合 |
| 10 | 50,000 | 5,000 | 大段连续 | 适合 |
| 5,000 | 50,000 | 10 | 碎片连续 | 非常不适合 |
- estimated_memory : 估算内存占用,这是核心决策指标
memory_run: 如果用 RunContainer,需要多少内存, ≈ run_count × RUN_ENTRY_SIZE(4 bytes)
memory_array: 如果用 ArrayContainer,需要多少内存, ≈ 元素数量 × 2 bytes
memory_bitmap: 如果用 BitmapContainer,需要多少内存,固定内存 = 8192 bytes
▶ 选择逻辑(简化)
if memory_run < min(memory_array, memory_bitmap):
use RunContainer
else if memory_array < memory_bitmap:
use ArrayContainer
else:
use BitmapContainer
| 转换方向 | 触发条件 |
|---|---|
| Array → Bitmap | 元素数量 > 4096 |
| Bitmap → Array | 元素数量 ≤ 4096 |
| 任意 → Run | 连续区间压缩明显 |
| Run → Array / Bitmap | run 碎片化或内存劣化 |
| 批量运算 | 根据结果自动选择 |
| optimize | 强制重新评估 |
- ✅ 在 Roaring 的自动选择机制下,正常情况下单个 container 的内存占用会被控制在 ≈ 8KB 左右或更小,在“理论极限完全填满65535个 container”情况下,大约占 520 MB 内存(除了数据本身还有一些元数据)
| 项目 | 估算 |
|---|---|
| Bitmap 数据 | ~512 MB |
| Container 元数据 | ~4 MB |
| 索引结构 | ~1 MB |
| Redis Key 自身 | ~0.1 MB |
| 合计 | 约 517 MB |

- 直观对比表
| 维度 | Redis Bitmap | redis-roaring |
|---|---|---|
| 存储结构 | 连续 bit array | 压缩 bitmap(分块) |
| 稀疏数据内存 | 极差 | 极优 |
| 最大 offset | 越大内存越爆 | 几乎无影响 |
| BITCOUNT / BITOP | 扫描整个 bitmap | 只扫描有效 container |
| 扩容成本 | 可能大规模 realloc | 局部 container 扩展 |
| 单 bit 读写 | 极快 | 略慢(结构层) |
| 模块依赖 | 原生 | Redis Module |
| 运维复杂度 | 最低 | 稍高 |
RedisRoaring核心价值与优势
-
- 内存效率更高
- Roaring Bitmap 会根据数据稀疏/密集自动选择最优表示方式(数组、位图或运行长度编码),显著减少内存占用,相比传统 Redis Bitmap 在稀疏数据上能节省大量空间。
-
- 高性能位操作
- 对单个位的操作保持 O(1) 性能,与 Redis 原生位图操作一致。
- 对于大规模操作(例如 BITOP AND/OR/XOR)性能显著优于原生位图,某些操作可达到 8 倍性能提升。
-
- 结构丰富的命令集
- 除了传统位图命令之外,redis-roaring 提供更丰富的数据操作命令,支持批量和分析型用例。
安装 RedisRoaring
安装时需要科学上网,主要是安装依赖时需要从海外网下载,如果要部署在国内服务器,可能会连接失败。
可以在海外的相同配置的服务器上进行编译,之后将编译好的redistimeseries.so上传到国内服务器即可。
mkdir -p /usr/local/soft/modules/
cd /usr/local/soft/modules
# clone 代码
git clone https://github.com/aviggiano/redis-roaring.git
cd redis-roaring/
# 推荐切换到稳定的release版本
git checkout v1.7.1
# 编译构建,作者将下载子模块、编译安装等命令都封装到一个脚本中,这里直接执行脚本即可
./configure.sh
# 如果没有错误,则说明编译成功,输出: dist/libredis-roaring.so
- 实际上
redis-roaring在构建后会在dist目录下生成好 Redis 相关命令和配置文件,如果没有安装Redis则可以直接使用。v1.7.1版本提供的Redis的版本为8.2.1,但此时是不带 Stack 相关模块的。
Redis 启用模块
- 将生成的
libredis-roaring.so拷贝到 redis 的 modules 目录下(非必须),目录不存在则创建
# 注意 .so 文件需要包含可执行权限
cp dist/libredis-roaring.so /usr/local/soft/redis-7.4.7/modules/libredis-roaring.so
- 本文采用
loadmodule加载模块
# 将 libredis-roaring.so 添加到 redis.conf 中,需要重启 redis
loadmodule /usr/local/soft/redis-7.4.7/modules/libredis-roaring.so
# 启动redis
redis-server redis.conf
# 登录测试
redis-cli --user admin --pass 123456
# 查看模块
127.0.0.1:6379> MODULE LIST
# 输出
1) 1) "name"
2) "ReJSON"
3) "ver"
4) (integer) 20816
5) "path"
6) "/usr/local/soft/redis-7.4.7/modules/rejson.so"
7) "args"
8) (empty array)
2) 1) "name"
2) "timeseries"
3) "ver"
4) (integer) 11209
5) "path"
6) "/usr/local/soft/redis-7.4.7/modules/redistimeseries.so"
7) "args"
8) (empty array)
3) 1) "name"
2) "search"
3) "ver"
4) (integer) 21025
5) "path"
6) "/usr/local/soft/redis-7.4.7/modules/redisearch.so"
7) "args"
8) (empty array)
4) 1) "name"
2) "bf"
3) "ver"
4) (integer) 20817
5) "path"
6) "/usr/local/soft/redis-7.4.7/modules/redisbloom.so"
7) "args"
8) (empty array)
5) 1) "name"
2) "REDIS-ROARING"
3) "ver"
4) (integer) 10700
5) "path"
6) "/usr/local/soft/redis-7.4.7/modules/libredis-roaring.so"
7) "args"
8) (empty array)
更多推荐




所有评论(0)