RoaringBitmap 原理
前言
位图索引被广泛用于数据库和搜索引擎中,通过利用位级并行,它们可以显著加快查询速度。但是,位图索引会占用大量的内存,因此我们会更喜欢压缩位图索引。 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 原理相关推荐
- Redis bitmap、hyperlog、布隆过滤器、RoaringBitmap原理应用场景与日活的统计的具体应用
传统方案-mysql 缺点: 1.空间占用大 2.统计逻辑复杂,比如 统计最近 30 天用户的累计活跃天(每个用户在 30 天里有 N 天使用 app,N 为 1-30,然后将月活跃用户的 N 天加总 ...
- Redis bitmap数据格式优化 (RoaringBitmap)
1. Key-Value 存储 尝试插入1kw条数据, key为设备MD5值, value为1, 此时Redis中存在1kw条key-value键值对. 通过info指令查看内存占用: 1kw数据ke ...
- RoaringBitMap在ClickHouse和Spark之间的实践-解决数据仓库预计算多维分析问题
前面在Spark多维分析去重计数场景优化案例中说了一下Spark计算在多维分析场景中的弊端,多维度分析会导致数据量指数级膨胀,搭配上去重计算字段越多,膨胀倍数也是线性增长,通过BitMap这个案例 ...
- 微服务架构系列文章、精品教程
python工具 python相关工具使用技巧(pip) virtualenv虚拟独立python环境使用 Python下APScheduler的快速指南 python文件夹,文件监听工具(pyino ...
- Redis Module 模块组件
介绍 首先介绍下RedisMod这个东西,它是一系列Redis的增强模块.有了RedisMod的支持,Redis的功能将变得非常强大.目前RedisMod中大体包含了如下增强模块: RediSearc ...
- 一文搞定ClickHouse在苏宁用户画像场景的实践(建议收藏)
关注公众号,获取更多一线大厂最新资讯! 摘要:今天分享的主要内容是ClickHouse在苏宁用户画像场景的实践 分享时间:2021年5月26日 内容分享:杨兆辉 摘要整理:皮卡丘 主要内容: 苏宁如何 ...
- 【java】高效压缩位图RoaringBitmap的原理与应用
文章目录 1.概述 2.位图法简述 3.RoaringBitmap的思路 4.Container原理 4.1 ArrayContainer 4.2 BitmapContainer 4.3 RunCon ...
- 高效压缩位图RoaringBitmap的原理与应用
目录 位图法简述 RoaringBitmap的思路 Container原理 ArrayContainer BitmapContainer RunContainer 时空分析 Container的创建与 ...
- EWAHCompressedBitmap数据结构及原理
EWAH 意思是 Enhanced Word-Aligned Hybrid,在WAH基础上优化而来. EWAH 算法论文:<Sorting improves word-aligned bitma ...
最新文章
- c++11随机数产生器default_random_engine
- PHP——模糊匹配文件|目录
- Hadoop-2.2.0学习之一Hadoop-2.2.0变化简介
- ‘FactorAnalyzer‘ object has no attribute ‘analyze‘和fa.loadings改成fa.loadings报错解决
- 垃圾回收GC Roots
- python自动化测试——测试报告以附件形式发送邮件
- SQL Server 执行计划利用统计信息对数据行的预估原理二(为什么复合索引列顺序会影响到执行计划对数据行的预估)...
- 10个CSS简写/优化技巧
- Android TTS(TextToSpeech)实践
- Ameya:蔡司激光共聚焦显微镜的优势特点及应用领域
- python的key函数_由 sort 中 key 的用法浅谈 python
- OSPF高等特性——Forwarding Address
- Splay Tree伸展树
- 我自己对英语学习的心得与体会
- ABAP 常用BAPI
- 【深度学习】平移不变性 (translation invariant)
- 如何解决缓存雪崩、击穿、穿透难题?
- LINUX下的用户访问授权,文件权限
- 为了快 0.00007 秒,有家交易公司花 1400 万美元买了块地
- 基于运放的放大电路分析
热门文章
- 计算器排html页面,简易计算器html页面代码
- flex布局实现垂直居中
- Node.js异步编程~超级详细哦
- php7 错误处理,PHP7升级-异常错误处理
- arcgis加载天地图_【arcgis地图实战】之天地图在线服务加载
- 日志分析里面的max是什么_mysql慢日志文件分析处理
- vue+axios 前端实现登录拦截(路由拦截、http拦截)
- cmd批量修改文件名 增加文字_Windows批处理脚本:以批量修改文件名为例
- c# 操作redisclient 设置过期时间_C# Redis分布式锁 单节点
- braintree php 开发,PHP关于Braintree支付