当前位置: 首页 > article >正文

Starrocks中RoaringBitmap杂谈

背景

最近在阅读Starrocks源码的时候,遇到ColumnRefSetRoaringBitmap使用,所以借此来讨论一下RoaringBitmap这个数据结构,这种思想是很值得借鉴的。
对于的实现可以参考一下

<dependency><groupId>org.roaringbitmap</groupId><artifactId>RoaringBitmap</artifactId><version>1.3.0</version>
</dependency>

的实现

杂谈

RoaringBitmap是高效压缩位图,简称RBM,我们可以通过Github RoaringBitmap了解它的全貌。

实现思路

  • 将 32bit int(无符号的)类型数据 划分为 2^16 个桶,即2^16=65536个桶,每个桶内用container来存放一个数值的低16位
  • 在存储和查询数值时,将数值划分为高16位和低16位,取高 16 位值找到对应的桶,然后在将低 16 位值存放在相应的 Container 中(存储时如果找不到就会新建一个)

举个例子:
以十进制数字131122为例,现在我们要将该数字放入到RBM中。第一步,先将该数字转换为16进制,131122对应的十六进制为0x00020032;其中,高十六位对应0x0002,首先我们找到0x0002所在的桶,再将131122的低16位存入到对应的container中,131122的低16位转换为10进制就是50,没有超过ArrayContainer的容量4096,所以将低16位直接放入到对应的ArrayContainer中。
在这里插入图片描述

如果要插入的数字低16位超过了4096,RBM会将ArrayContainer转换为BitMapContainer

具体的操作

摘抄自Github官网,如下

import org.roaringbitmap.RoaringBitmap;public class Basic {public static void main(String[] args) {RoaringBitmap rr = RoaringBitmap.bitmapOf(1,2,3,1000);RoaringBitmap rr2 = new RoaringBitmap();rr2.add(4000L,4255L);rr.select(3); // would return the third value or 1000rr.rank(2); // would return the rank of 2, which is index 1rr.contains(1000); // will return truerr.contains(7); // will return falseRoaringBitmap rror = RoaringBitmap.or(rr, rr2);// new bitmaprr.or(rr2); //in-place computationboolean equals = rror.equals(rr);// trueif(!equals) throw new RuntimeException("bug");// number of values stored?long cardinality = rr.getLongCardinality();System.out.println(cardinality);// a "forEach" is faster than this loop, but a loop is possible:for(int i : rr) {System.out.println(i);}}
}

container的类型

小桶的实现目前有三种:ArrayContainer,BitmapContainer,RunContainer。默认采用 ArrayContainer

  • ArrayContainer
    这个是 RoaringBitmap 默认小桶的实现,在初始化的时候,会初始化长度为4的ArrayContainer
    其内部实现是用 Char数组实现的

    public ArrayContainer(int capacity) {this.cardinality = 0;this.content = new char[capacity];
    }
    

    其中每个Char占用两个字节。
    从Add方法来看:

    @Override
    public Container add(final char x) {if (cardinality == 0 || (cardinality > 0&& (x) > (content[cardinality - 1]))) {if (cardinality >= DEFAULT_MAX_SIZE) {return toBitmapContainer().add(x);}if (cardinality >= this.content.length) {increaseCapacity();}content[cardinality++] = x;} else {int loc = Util.unsignedBinarySearch(content, 0, cardinality, x);if (loc < 0) {// Transform the ArrayContainer to a BitmapContainer// when cardinality = DEFAULT_MAX_SIZE // DEFAULT_MAX_SIZE值为4096if (cardinality >= DEFAULT_MAX_SIZE) {return toBitmapContainer().add(x);}if (cardinality >= this.content.length) {increaseCapacity();}// insertion : shift the elements > x by one position to// the right// and put x in it's appropriate placeSystem.arraycopy(content, -loc - 1, content, -loc, cardinality + loc + 1);content[-loc - 1] = x;++cardinality;}}return this;
    }
    
    • ArrayContainer内部的数据是排序的
    • 容量超过4096(这个是代码写死的)后,会转换为BitmapContainer
    • ArrayContainer占用的内存空间为 4096*2B ,即 8KB
  • BitmapContainer
    这个就是一个位图,这里的位图的长度为 2^16 ,也就是占用 2^16 bit,所有占用存储为8KB

  • RunContainer
    这是一种利用步长来压缩空间的方法,
    比如连续的整数序列 11, 12, 13, 14, 15, 27, 28, 29 会被 压缩为两个二元组 11, 4, 27, 2 表示:11后面紧跟着4个连续递增的值,27后面跟着2个连续递增的值,那么原先16个字节的空间,现在只需要8个字节,这种用的比较少

可以看到 ArrayContainer 占用的内存的最大空间为 8KB,和BitMapContainer占用的空间内存一样,但是ArrayContainer存储的数据最大为4096,超过这个以后,内存空间的占用就会超过8KB,所以从内存占用考虑的话,ArrayContainer适合存储稀疏数据,适合存储稠密数据,这样策略下,能够最大程度的避免内存浪费

查询的性能

和BitMap相比
  • Roaringbitmap本质上是将大块分为了各个小块,并且只有小块有数据的时候才会存在,所以Roaringbitmap在前16位的时候,就可以将部分数据过滤掉,而不像 BitMap一样,所有的位都需要进行计算

其他

除了 32位的RoaringBitmap外,还有64位的Roaring64Bitmap,如下:

    import org.roaringbitmap.longlong.*;// first Roaring64NavigableMapLongBitmapDataProvider r = Roaring64NavigableMap.bitmapOf(1,2,100,1000);r.addLong(1234);System.out.println(r.contains(1)); // trueSystem.out.println(r.contains(3)); // falseLongIterator i = r.getLongIterator();while(i.hasNext()) System.out.println(i.next());// second Roaring64Bitmapbitmap1 = new Roaring64Bitmap();bitmap2 = new Roaring64Bitmap();int k = 1 << 16;long i = Long.MAX_VALUE / 2;long base = i;for (; i < base + 10000; ++i) {bitmap1.add(i * k);bitmap2.add(i * k);}b1.and(bitmap2);
http://www.lryc.cn/news/2401206.html

相关文章:

  • 通过ca证书的方式设置允许远程访问Docker服务
  • 涂胶协作机器人解决方案 | Kinova Link 6 Cobot在涂胶工业的方案应用与价值
  • 理解继承与组合的本质:Qt 项目中的设计选择指南
  • 新手小白使用VMware创建虚拟机安装Linux
  • 使用 PHP 和 Guzzle 对接印度股票数据源API
  • EscapeX:去中心化游戏,开启极限娱乐新体验
  • 使用PyQt5的图形用户界面(GUI)开发教程
  • STM32实战:智能环境监测站设计方案
  • 猎板硬金镀层厚度:新能源汽车高压系统的可靠性基石
  • KEYSIGHT是德科技 E5063A 18G ENA系列网络分析仪
  • VR 虚拟仿真工器具:开启医学新视界的智慧钥匙​
  • webshell管理工具、C2远控服务器流量分析
  • JavaWeb:前端工程化-TS(TypeScript)
  • unity+ spine切换武器不换皮肤解决方案
  • [java八股文][MySQL面试篇]SQL基础
  • Ubuntu中SSH服务器安装使用
  • 【AI论文】SWE-rebench:一个用于软件工程代理的任务收集和净化评估的自动化管道
  • Flask文件处理全攻略:安全上传下载与异常处理实战
  • 【算法深练】分组循环:“分”出条理,化繁为简
  • 焊缝缺陷焊接缺陷识别分割数据集labelme格式5543张4类别
  • 关于scrapy在pycharm中run可以运行,但是debug不行的问题
  • Java高级 | 【实验四】Springboot 获取前端数据与返回Json数据
  • 云数据库选型指南:关系型 vs NoSQL vs NewSQL的企业决策
  • Prj08--8088单板机C语言8255读取按键码
  • 蜜獾算法(HBA,Honey Badger Algorithm)
  • Modbus转Ethernet IP网关助力罗克韦尔PLC数据交互
  • 飞算JavaAI 炫技赛重磅回归!用智能编码攻克老项目重构难题
  • 青少年编程与数学 02-020 C#程序设计基础 15课题、异常处理
  • Electron打包前端和后端为exe
  • unix/linux,sudo,一个强大且灵活的工具,允许一个被授权的用户以另一个用户(通常是root,即超级用户)的身份来执行命令