Range-Encoded Bit-Slice Index

本文详细介绍了Bit-SlicedRange-EncodedBitmap算法,一种改进的bitmap结构,增强了范围查询能力和空间效率。通过Bit-slicedIndex和Range-EncodedBit-SliceIndex解决原始问题,尤其讨论了十进制和二进制表示法以及RoaringBitmap的开源实现。
摘要由CSDN通过智能技术生成

1 简述

  算法使用Bit-Sliced Range-Encoded BitMap算法,在基础bitmap的基础上,添加了范围查询和Bit-Sliced
  范围查询用来弥补bitmap范围查询能力弱的问题;Bit-Sliced用来降低索引Key值的存储数量,进而降低整体索引的空间占用
  BSI就是Bit-Sliced,即将数值按位切分表示,用更少的内容代表更多的数据,通常有十进制方式和二进制方式,二进制方式更佳
  参考资料:1、https://www.featurebase.com/blog/range-encoded-bitmaps;2、https://neway6655.github.io/bitmap/2021/06/14/bitmap_with_range_query.html

2 算法构建原理

2.1 Bitmap

  行的数值就是可能取值,每个数据为哪个值就在对应行标1,其他行标0
  适用于等值查询,只需要取对应行的bit数组,数组中标1的即为所需的结果
  缺点:1、范围查询能力弱,需要把所有小于该值的行的结果取并集;2、行数多,需要穷举所有的可能值在这里插入图片描述

2.2 Range-Encoded Bitmap

  在bitmap的基础上进行一层简单变化,将本来等于该值的行标1改为将所有大于该值的行标1,增强范围查询的能力

image.png

2.3 Bit-sliced Index

  解决行数多的问题,将行代表的索引值改为分片表示,通常为十进制或者二进制表示法

image.png

2.4 Range-Encoded Bit-Slice Index

  就是把Range-Encoded和Bit-Slice相结合,解决了原始bitmap的两个缺陷

2.4.1 十进制表示法

  • 基础变化

image.png

  • 进阶变化

image.png

2.4.2 二进制表示法

  • 基础变化

image.png

  • 进阶变化

image.png

3 算法查询原理

3.1 十进制表示

  以两位数举例
  <=78的集合为:(comp 1)-6 ∪ ((comp 1) - 7) ∩ (comp 0) - 8),也就是 (十位为6的bit数组) | ((十位为7的bit数组) & (个位为8的bit数组))
  补充:以上算法的意义就是<=69的数据加上 (7078、6068、…)
  >78的集合就是<=78的集合取反(不支持取反,或者范围查询,那就是<=99的集合减去<=78的集合)
  =78的集合就是<=78的集合减去<=77的集合

3.2 二进制表示

  对于二进制,需要对查询数据的位分0、1讨论
  对于二进制某一位行值上,为1就表示该位上原始数据为0,为0表示该位上原始数据为1。因此,如果查询数据该位为0,则仅针对该行来说,可以推导出行值上为0代表的列必然大于查询数据;同理可以推导,如果查询数据该位为1,则行上为1代表的列必然小于查询数据
  以上推导仅对单行来说(或者说对最高位来说),整体的查询判断还需要对各行的结果进行复杂的聚合
  可以明确的是,高位确定大于的列,不论低位如何,最终结果都是大于的;高位确定小于的列,不论低位如何,最终结果肯定是小于的;因此整体判断需要由高位向低位推进
  以<400来说,400对应为:0110010000,列编号为0开始

  • comp 9:确定[3]>400
  • comp 8:结合高位结果,确定[0,2,4]<400
  • comp 7:[0,2]<400,结合高位,还是[0,2,4]<400
  • comp 6:单行来说,应该有[4]>400,但是高位结果优先,高位判断[4]<400,所以此处最终结果还是只有[3]>400
    以此类推,获取等于的值只需要去除大于和小于的集合就行了

4 代码实现

4.1 说明

  Roaring bitmaps有开源实现:https://github.com/RoaringBitmap/RoaringBitmap/tree/master/bsi
  目前看接口处理的参数都是int类型,所以数据量和索引值都限定在int范围内
  数据量肯定没有负数,理论上索引值是可以为负数的,但目前看整个算法的逻辑是以正数建立的,对负数的处理存在问题,所以限定索引值也为正数
  索引实现类是RoaringBitmapSliceIndex,查询结果表示类为RoaringBitmap,分别引入依赖
  优势:算法来源RoaringBitmap的子模块,除了实现了算法外,RoaringBitmap本身就是一种高效的bitmap压缩算法,也同时解决了bitmap压缩的问题

<dependency>
  <groupId>org.roaringbitmap</groupId>
  <artifactId>bsi</artifactId>
  <version>0.9.46</version>
</dependency>
<dependency>
  <groupId>org.roaringbitmap</groupId>
  <artifactId>RoaringBitmap</artifactId>
  <version>0.9.46</version>
</dependency>

  RoaringBitmapSliceIndex核心是两个接口:setValue和compare
  setValue接口用于设置数据,也就是创建索引的过程,参数为列编号和该列的数值(列编号无所谓从0从1开始,只要整个流程统一即可)

public void setValue(int columnId, int value)

  compare接口用于查询数据,有四个参数,第一个是比较类型;第二个和第三个是比较用的参数,其中第三个参数仅用于范围查询时使用,否则忽略;第四个参数一般不用,设定哪些列参与比较;
  此外,compare过程中还加入了最大最小值比较以加快查询速度

public RoaringBitmap compare(BitmapSliceIndex.Operation operation, int startOrValue, int end, RoaringBitmap foundSet)

  查询的返回值RoaringBitmap可以进行位运算,所以可以对多个列的返回值进行聚合操作,取并集交集等

4.2 样例

RoaringBitmapSliceIndex bsi = new RoaringBitmapSliceIndex();
bsi.setValue(0, 3);
bsi.setValue(1, 392);
bsi.setValue(2, 47);
bsi.setValue(3, 956);
bsi.setValue(4, 219);
// 获取小于400的列
RoaringBitmap result = bsi.compare(BitmapSliceIndex.Operation.LT, 400, 0, null);
for (int i:result.toArray()) {
    System.out.print(i+",");
}
System.out.println();
// 获取大于300的列
RoaringBitmap result2 = bsi.compare(BitmapSliceIndex.Operation.GT, 300, 0, null);
for (int i:result2.toArray()) {
    System.out.print(i+",");
}
System.out.println();
// 多个查询结果做and操作,获取大于300且小于400的列
result.and(result2);
for (int i:result.toArray()) {
    System.out.print(i+",");
}
System.out.println();
// 判断是否有满足需求的列
System.out.println(result.isEmpty());
// 获取>=219 & <=392的值
RoaringBitmap resultRange = bsi.compare(BitmapSliceIndex.Operation.RANGE, 219, 392, null);
for (int i:resultRange.toArray()) {
    System.out.print(i+",");
}
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值