前言

位图索引被广泛用于数据库和搜索引擎中,通过利用位级并行,它们可以显著加快查询速度。但是,位图索引会占用大量的内存,因此我们会更喜欢压缩位图索引。 Roaring Bitmaps 就是一种十分优秀的压缩位图索引,我们统称 RBM。

这是之前在学习ElasticSearch的时候注意到的一种有意思的算法,整合了对几位大佬文章的理解,谈一下自己肤浅浅浅浅+65535的看法(bushi

前置知识

Bitmap

Bitmap的基本原理就是用一个bit 位来存放某种状态,适用于大规模数据,但数据状态又不是很多的情况。通常是用来判断某个数据存不存在的。以1表示存在,0表示不存在。

对于一个8位的Bitmap
如果对于(0,2,4,6)四个数
Value 1 0 1 0 1 0 1 0
Index 0 1 2 3 4 5 6 7

 而对于一个bitmap来说,所占内存大小仅仅跟位数有关

1byte=8bit
1kb=1024byte

对于一个16位的bitmap
所占内存大小=2^16/8/1024=8k

Array

这里我们只提一下内存情况,比较容易理解,Array的内存大小是随着元素个数增长而线性增长的

为什么要提内存情况后边会有涉及


RBM

我们以存放 Integer 值的 Bitmap 来举例,RBM 把一个 32 位的 Integer 划分为高 16 位和低 16 位,通过高 16 位找到该数据存储在哪个桶中(高 16 位可以划分 2^16 个桶),把剩余的低 16 位放入该桶对应的 Container 中。

每个桶都有对应的 Container,不同的 Container 存储方式不同。依据不同的场景,主要有 2 种不同的 Container,分别是 Array Container 和 Bitmap Container。Array Container 存放稀疏的数据,Bitmap Container 存放稠密的数据。若一个 Container 里面的元素数量小于 4096,使用 Array Container 来存储。当 Array Container 超过最大容量 4096 时,会转换为 Bitmap Container

举个例子:

例如,0x00020032(十进制131122)放入一个 RBM 的过程如下图所示

 

0x00020032 的前 16 位是 0002,找到对应的桶 0x0002。在桶对应的 Container 中存储低 16 位,因为 Container 元素个数不足 4096,因此是一个 Array Container。低 16 位为 0032(十进制为50), 在 Array Container 中二分查找找到相应的位置插入即可(如上图50的位置)。

相较于原始的 Bitmap 需要占用 16K (131122/8/1024) 内存来存储这个数,而这种存储实际只占用了4B(桶中占 2 B,Container中占 2 B,不考虑数组的初始容量)。

使用bitmap容器也是类似

容器选择

上面我们提到了一个关于容器选择的阈值4096,那为什么当容量为4096时会成为阈值呢?

这里有大佬画了一张很形象的图

Redisson JsonJacksonCodec 压缩 redis bitmap压缩_位图

 结合在前置知识中提到的内存问题,本算法的容器切换可以使得内存成本得到节省


总结

以上是本人对于RMB算法的一些浅薄理解