前言

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

压缩位图索引有很多种,比如基于 RLE(Run-Length
Encoding,运行长度编码)的WAH (Word Aligned Hybrid Compression Scheme) 和 Concise (Compressed ‘n’ Composable Integer Set)。相比较前者, RBM 能提供更优秀的压缩性能和更快的查询效率。

用途

RBM 的用途和 Bitmap 很差不多(比如说索引),只是说从性能、空间利用率各方面更优秀了。目前 RBM 已经在很多成熟的开源大数据平台中使用,简单列几个作为参考:

Apache Lucene and derivative systems such as Solr and Elasticsearch,
Metamarkets’ Druid,
Apache Spark,
Apache Hive,
eBay’s Apache Kylin,
……

总之 RBM 很优秀,大家都在用,学一学可能自己写代码用不到,但是对于理解这些常用的开源大数据系统没有坏处。

主要思想

RBM 的主要思想并不复杂,简单来讲,有如下三条:

  • 我们将 32-bit 的范围 ([0, n)) 划分为 2^16 个桶,每一个桶有一个 Container 来存放一个数值的低16位;
  • 在存储和查询数值的时候,我们将一个数值 k 划分为高 16 位(k % 2^16)和低 16 位(k mod 2^16),取高 16 位找到对应的桶,然后在低 16 位存放在相应的 Container 中;
  • 容器的话, RBM 使用两种容器结构: Array Container 和 Bitmap Container。Array Container 存放稀疏的数据,Bitmap Container 存放稠密的数据。即,若一个 Container 里面的 Integer 数量小于 4096,就用 Short 类型的有序数组来存储值。若大于 4096,就用 Bitmap 来存储值。

三种Container

下面介绍到的是RoaringBitmap的核心,三种Container。

通过上面的介绍我们知道,每个32位整形的高16位已经作为key存储在RoaringArray中了,那么Container只需要处理低16位的数据。

ArrayContainer

static final int DEFAULT_MAX_SIZE = 4096short[] content;

结构很简单,只有一个short[] content,将16位value直接存储。

short[] content始终保持有序,方便使用二分查找,且不会存储重复数值。

因为这种Container存储数据没有任何压缩,因此只适合存储少量数据。

ArrayContainer占用的空间大小与存储的数据量为线性关系,每个short为2字节,因此存储了N个数据的ArrayContainer占用空间大致为2N字节。存储一个数据占用2字节,存储4096个数据占用8kb。

根据源码可以看出,常量DEFAULT_MAX_SIZE值为4096,当容量超过这个值的时候会将当前Container替换为BitmapContainer。

BitmapContainer

final long[] bitmap;

这种Container使用long[]存储位图数据。我们知道,每个Container处理16位整形的数据,也就是0~65535,因此根据位图的原理,需要65536个比特来存储数据,每个比特位用1来表示有,0来表示无。每个long有64位,因此需要1024个long来提供65536个比特。

因此,每个BitmapContainer在构建时就会初始化长度为1024的long[]。这就意味着,不管一个BitmapContainer中只存储了1个数据还是存储了65536个数据,占用的空间都是同样的8kb。

解释一下为什么这里用的 4096 这个阈值?因为一个 Integer 的低 16 位是 2Byte,因此对应到 Arrary Container 中的话就是 2Byte * 4096 = 8KB;同样,对于 Bitmap Container 来讲,2^16 个 bit 也相当于是 8KB。

RunContainer

private short[] valueslength;int nbrruns = 0;

RunContainer中的Run指的是行程长度压缩算法(Run Length Encoding),对连续数据有比较好的压缩效果。

它的原理是,对于连续出现的数字,只记录初始数字和后续数量。即:

对于数列11,它会压缩为11,0;
对于数列11,12,13,14,15,它会压缩为11,4;
对于数列11,12,13,14,15,21,22,它会压缩为11,4,21,1;
源码中的short[] valueslength中存储的就是压缩后的数据。

这种压缩算法的性能和数据的连续性(紧凑性)关系极为密切,对于连续的100个short,它能从200字节压缩为4字节,但对于完全不连续的100个short,编码完之后反而会从200字节变为400字节。

如果要分析RunContainer的容量,我们可以做下面两种极端的假设:

  • 最好情况,即只存在一个数据或只存在一串连续数字,那么只会存储2个short,占用4字节
  • 最坏情况,0~65535的范围内填充所有的奇数位(或所有偶数位),需要存储65536个short,128kb

Container性能总结

读取时间

只有BitmapContainer可根据下标直接寻址,复杂度为O(1),ArrayContainer和RunContainer都需要二分查找,复杂度O(log n)

内存占用

其中,

  • ArrayContainer一直线性增长,在达到4096后就完全比不上BitmapContainer了
  • BitmapContainer是一条横线,始终占用8kb
  • RunContainer比较奇葩,因为和数据的连续性关系太大,因此只能画出一个上下限范围。不管数据量多少,下限始终是4字节;上限在最极端的情况下可以达到128kb。

位运算

RBM 基于前面提到的两种 Container,在两个 Container 之间的 Union (bitwise OR) 或者 Intersection (bitwise AND) 操作又会出现下面三种场景:

Bitmap vs Bitmap
Bitmap vs Array
Array vs Array

RBM 提供了相应的算法来高效地实现这些操作。

RoaringBitmap针对Container的优化策略

创建时:

  • 创建包含单个值的Container时,选用ArrayContainer
  • 创建包含一串连续值的Container时,比较ArrayContainer和RunContainer,选取空间占用较少的

转换:

针对ArrayContainer:

  • 如果插入值后容量超过4096,则自动转换为BitmapContainer。因此正常使用的情况下不会出现容量超过4096的ArrayContainer。
  • 调用runOptimize()方法时,会比较和RunContainer的空间占用大小,选择是否转换为RunContainer。

针对BitmapContainer:

  • 如果删除某值后容量低至4096,则会自动转换为ArrayContainer。因此正常使用的情况下不会出现容量小于4096的BitmapContainer。
  • 调用runOptimize()方法时,会比较和RunContainer的空间占用大小,选择是否转换为RunContainer。

针对RunContainer:

  • 只有在调用runOptimize()方法才会发生转换,会分别和ArrayContainer、BitmapContainer比较空间占用大小,然后选择是否转换。

RoaringBitmap 原理相关推荐

  1. Redis bitmap、hyperlog、布隆过滤器、RoaringBitmap原理应用场景与日活的统计的具体应用

    传统方案-mysql 缺点: 1.空间占用大 2.统计逻辑复杂,比如 统计最近 30 天用户的累计活跃天(每个用户在 30 天里有 N 天使用 app,N 为 1-30,然后将月活跃用户的 N 天加总 ...

  2. Redis bitmap数据格式优化 (RoaringBitmap)

    1. Key-Value 存储 尝试插入1kw条数据, key为设备MD5值, value为1, 此时Redis中存在1kw条key-value键值对. 通过info指令查看内存占用: 1kw数据ke ...

  3. RoaringBitMap在ClickHouse和Spark之间的实践-解决数据仓库预计算多维分析问题

    ​ 前面在Spark多维分析去重计数场景优化案例中说了一下Spark计算在多维分析场景中的弊端,多维度分析会导致数据量指数级膨胀,搭配上去重计算字段越多,膨胀倍数也是线性增长,通过BitMap这个案例 ...

  4. 微服务架构系列文章、精品教程

    python工具 python相关工具使用技巧(pip) virtualenv虚拟独立python环境使用 Python下APScheduler的快速指南 python文件夹,文件监听工具(pyino ...

  5. Redis Module 模块组件

    介绍 首先介绍下RedisMod这个东西,它是一系列Redis的增强模块.有了RedisMod的支持,Redis的功能将变得非常强大.目前RedisMod中大体包含了如下增强模块: RediSearc ...

  6. 一文搞定ClickHouse在苏宁用户画像场景的实践(建议收藏)

    关注公众号,获取更多一线大厂最新资讯! 摘要:今天分享的主要内容是ClickHouse在苏宁用户画像场景的实践 分享时间:2021年5月26日 内容分享:杨兆辉 摘要整理:皮卡丘 主要内容: 苏宁如何 ...

  7. 【java】高效压缩位图RoaringBitmap的原理与应用

    文章目录 1.概述 2.位图法简述 3.RoaringBitmap的思路 4.Container原理 4.1 ArrayContainer 4.2 BitmapContainer 4.3 RunCon ...

  8. 高效压缩位图RoaringBitmap的原理与应用

    目录 位图法简述 RoaringBitmap的思路 Container原理 ArrayContainer BitmapContainer RunContainer 时空分析 Container的创建与 ...

  9. EWAHCompressedBitmap数据结构及原理

    EWAH 意思是 Enhanced Word-Aligned Hybrid,在WAH基础上优化而来. EWAH 算法论文:<Sorting improves word-aligned bitma ...

最新文章

  1. c++11随机数产生器default_random_engine
  2. PHP——模糊匹配文件|目录
  3. Hadoop-2.2.0学习之一Hadoop-2.2.0变化简介
  4. ‘FactorAnalyzer‘ object has no attribute ‘analyze‘和fa.loadings改成fa.loadings报错解决
  5. 垃圾回收GC Roots
  6. python自动化测试——测试报告以附件形式发送邮件
  7. SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)...
  8. 10个CSS简写/优化技巧
  9. Android TTS(TextToSpeech)实践
  10. Ameya:蔡司激光共聚焦显微镜的优势特点及应用领域
  11. python的key函数_由 sort 中 key 的用法浅谈 python
  12. OSPF高等特性——Forwarding Address
  13. Splay Tree伸展树
  14. 我自己对英语学习的心得与体会
  15. ABAP 常用BAPI
  16. 【深度学习】平移不变性 (translation invariant)
  17. 如何解决缓存雪崩、击穿、穿透难题?
  18. LINUX下的用户访问授权,文件权限
  19. 为了快 0.00007 秒,有家交易公司花 1400 万美元买了块地
  20. 基于运放的放大电路分析

热门文章

  1. 计算器排html页面,简易计算器html页面代码
  2. flex布局实现垂直居中
  3. Node.js异步编程~超级详细哦
  4. php7 错误处理,PHP7升级-异常错误处理
  5. arcgis加载天地图_【arcgis地图实战】之天地图在线服务加载
  6. 日志分析里面的max是什么_mysql慢日志文件分析处理
  7. vue+axios 前端实现登录拦截(路由拦截、http拦截)
  8. cmd批量修改文件名 增加文字_Windows批处理脚本:以批量修改文件名为例
  9. c# 操作redisclient 设置过期时间_C# Redis分布式锁 单节点
  10. braintree php 开发,PHP关于Braintree支付