精确去重和Roaring BitMap
精确去重和Roaring BitMap
互联网行业常见的一个业务需求就是求UV(日活)和N日留存,这就涉及到去重计数(COUNT DISTINCT)的计算.
BitMap概述
精确去重算法主要通过BitMap来实现,它本质上是定义了一个很大的 bit 数组,每个元素对应到 bit 数组的其中一位
一个Integer是32-bit, 一共有Integer.MAX_VALUE = 2 ^ 32个值,对于原始的Bitmap来说,这就需要2 ^ 32长度的bit数组
通过计算可以发现(2 ^ 32 / 8 bytes = 512MB), 一个普通的Bitmap需要耗费512MB的存储空间
不管业务值的基数有多大,这个存储空间的消耗都是恒定不变的,这显然是不能接受的
而Roaring Bitmap作为压缩性能更好的位图索引,广泛应用于众多成熟的开源大数据平台(Kylin、Druid、ES等)
对于非Integer类型的数据(比如String类型),可以通过数据字典映射成Integer
Roaring Bitmap的数据结构
- Roaring bitmap用来表示所有32-bit的unsigned integer的集合(共2 ^ 32 = 42 9496 7296个)
- 这个数足够覆盖一款产品的用户量了
Roaring bitmap的数据结构为Key-Value的键值对:
- Key: 根据业务值整数的high 16-bits进行分桶(共2 ^ 16 = 65536个), 每一个Roaring Bitmap的Key值(first-level)都存放在short(16-bit)类型的有序数组中:
short[] keys
- Value: 用于存放业务值整数的low 16-bits的一个container. 每一个Roaring Bitmap有一个Container数组:
Container[] values
三种不同类型的Container
Roaring bitmap共有三种不同类型的Container, 分别是Array Container, Bitmap Container, Run Container.
Array Container
Array Container通过16-bit unsigned integer的有序数组存放(short in Java)业务值
数组的初始长度为DEFAULT_INIT_SIZE = 4
, 扩容的规则为:
- 若 size < 64, 则capacity * 2;
- 若 64 < size < 1067, 则capacity * 1.5;
- 若 1067 < size, 则capacity * 1.25;
- 不会分配超过
DEFAULT_MAX_SIZE = 4096
大小的capacity; - 特别处理: 若size离最大值(4096)只差1/16时, 直接升舱到最大值(4096)
Array Container有一个counter用来追踪基数
序列化之后Array Container消耗的存储空间为2c+2
bytes, c表示基数
Bitmap Container
Bitmap Container通过固定size为1024的64-bit(long in Java)的数组存放业务值, 这个数组刚好能存下2 ^ 16个数字
有一个counter用来存放1的个数
序列化之后Bitmap Container消耗的存储空间固定为为8192
bytes
Run Container
Run Container通过存放pair<start_value, length>于16-bit integer(short in Java)的数组中来表示业务值, 例如11、12、13、14、15可以被优化成11, 4
其中start_value表示起始值, length为该起始值后连续1的长度
这个short数组的capacity也是动态分配
Run Container的基数可以通过SUM(length)计算出来, 没有用另外的counter进行追踪
序列化之后Run Container消耗的存储空间为2+4r
bytes, r表示pair<start_value, length>的数目
Contrainer之间的转换
- 如果开始的时候是一个空的Roaring bitmap, 那么当添加一个业务值时,Array Container就会被创建
- 当插入新的业务值时, 基数超过4096, Array Container会转化成Bitmap Container
- 通过
runOptimize
function触发原container的扫描, 决定是否转换为Run Container, 只有当存储消耗比Array Container或Bitmap Container小的时候, 才会发生转换
- 根据计算可以得出,基数大于4096的时候,
2 + 4r < 8192
, 那么不应超过2047个pair;- 基数小于4096的时候,pair的个数应该小于基数的一半
Roaring Bitmap逻辑操作的简单分析
在Roaring Bitmap中, 一个bitmap包含一个RoaringArray类型的成员变量highLowContainer, 用于存储数据: RoaringArray highLowContainer
RoaringArray包含两个数组, 分别是short[] keys
和Container[] values
Key值(first-level)的处理
每一个Roaring Bitmap的Key值(first-level)都存放在short(16-bit)类型的有序数组中
对两个bitmap做逻辑运算时,先做Key值的比较
依次迭代两个bitmap的key数组
若key值相同, 则对value的container进行逻辑计算, 不同类型的container有不同的操作, 计算结束后, 两个key数组迭代器步长+1
如果key值不同, key值较小的迭代器的步长(index), 会一直加到自己的key值恰好等于较大的key值(二分查找), 找不到则返回较小的数组的size
Find the smallest integer index larger than pos such that array[index].key=x. If none can be found, return size. Based on code by O. Kaser.
如果是Union计算, 在第3步较小的迭代器移动步长的过程中, 会把每次经过的key值和value值添加到作为结果的bitmap中
对于Union计算, 必须把两个数组都迭代完全; 对于Intersection计算, 只要有一个数组迭代完就可以结束了
Bitmap Container vs Bitmap Container的操作
对于Union操作,输入为两个bitmap container, 迭代两个container中的1024个long值(long[] bitmap
)
- 分别做位运算中的
|
操作, 得到一个新的long[] bitmap
数组 - 同时利用
Long.bitCount()
计算新的bitmap container的基数
对于Intersection操作, 同样输入两个bitmap container
- 先迭代两个container中的1024个long值(
long[] bitmap
)一次,计算交集的基数 - 基数大于4096, 则再次迭代两个container,分别做位运算中的
&
操作,得到一个新的long[] bitmap
数组 - 基数不大于4096,也再次迭代两个container,并将long值
&
操作的结果转换成short类型,存放在最终的结果Array Container中
Bitmap Container vs Array Container的操作
对于Union操作, 输入为一个bitmap container和一个array container
- 先把这个bitmap container复制一遍
- 遍历array container的数组, 将每个值v换算成复制后的bitmap container中
long[] bitmap
数组的对应下标i, 取出该bitmap的值bitmap[i]
和v做相关的|
操作,写入这个bitmap[i]
- 遍历完成后这个复制的bitmap container即为结果bitmap container
对于Intersection操作, 输入为一个bitmap container和一个array container
- 先创建一个和array container一样大小的新的array container
- 遍历旧的array container, 查找里面的值是否存在于bitmap container中
- 存在则写入新的array container的short[]数组中
- 这个新的array container即为交集结果
Array Container vs Array Container的操作
对于Union操作, 输入为两个array container
- 直接相加两个container的基数,若基数和 > 4096, 则把两个container的值置于一个新的bitmap container中计算,结果的基数如果不大于4096, 则把结果转换成array container返回,否则直接返回bitmap container
- 若基数和不大于4096, 则遍历两个数组,通过数组进行相关的Union计算
对于Intersection操作, 输入为两个array container
- 通过数组操作进行相关的Insection计算
参考文献
- Consistently faster and smaller compressed bitmaps with Roaring
- Better bitmap performance with Roaring bitmaps# 、
精确去重和Roaring BitMap相关推荐
- 将Roaring Bitmap序列化为JSON
近期在实现一个数据结构时使用到了位图索引(bitmap index)[1],本文就来粗浅聊聊位图(bitmap). 一. 什么是bitmap 位图索引使用位数组(bit array,也有叫bitset ...
- Flink+Hologres亿级用户实时UV精确去重最佳实践
简介:Flink+Hologres亿级用户实时UV精确去重最佳实践 UV.PV计算,因为业务需求不同,通常会分为两种场景: 离线计算场景:以T+1为主,计算历史数据 实时计算场景:实时计算日常新增的数 ...
- ClickHouse基于全局字典与物化视图的精确去重方案
clickhouse具有bitmap, 但只支持int, 实测表明groupBitmap()这个agg比直接的count(distinct x)计算要快至少一倍以上, 按之前druid中的测试 经验表 ...
- 位图—BitMap和BitSet,布隆过滤器,Roaring Bitmap
位图 简单来说就是为了压缩节省空间,才出现的. 举个例子: 你要是存储三个数字 2,5,10.这三个数字用java中的short类型来存储,也要6个Byte(short类型的内存空间是2Byte).一 ...
- Greenplum roaring bitmap与业务场景 (类阿里云RDS PG varbitx, 应用于海量用户 实时画像和圈选、透视)
摘要: 标签 PostgreSQL , Greenplum , varbitx , roaring bitmap , pilosa , varbit , hll , 多阶段聚合 背景 roaring ...
- Roaring Bitmap原理
Roaring Bitmap 最近面试字节,被问roaring bitmap原理,虽然之前看过,时间久了细节忘了,再次mark下,这篇文章讲的很透彻 Roaring Bitmap
- Spark多维分析去重计数场景优化案例【BitMap精确去重的应用与踩坑】
关注交流微信公众号:小满锅 场景 前几天遇到一个任务,从前也没太注意过这个任务,但是经常破9点了,执行时长正常也就2个小时. 看逻辑并不复杂,基本是几段SQL的JOIN操作,其中一个最耗时间的就是要根 ...
- 10亿手机号如何去重?(BitMap)
10亿手机号如何去重 方案一使用数组 方案二使用HashSet 方案三BitMap java.util.BitSet 分桶理论 方案一使用数组 一个数组存入10亿数据,第一位手机号一定为1则忽略. 如 ...
- 海量数据的非精确去重利器——从HyperLogLog到布谷鸟过滤器
背景 非精确:牺牲一定准确度换取空间效率和时间效率. 统计网站的UV(独立访客数):当用户数量非常多时,比如几千万甚至上亿,那么使用普通的哈希表去重将会占用可怕的巨大内存空间.引用吴军博士的<数 ...
最新文章
- python练习_Python随笔31:Python基础编程练习题27~28
- css根据文字长度实现宽度自适应
- 为什么我的文章没有被推荐?
- 每天一个linux命令(9):nl命令
- 页面调用系统window打印
- 掌握深度学习,为什么要用 PyTorch、TensorFlow 框架?
- 算法题目——生成括号匹配
- Linux 文件压缩解压缩
- 如何root安卓手机_如何从我的字体里面提取TTF并阉割成未Root安卓手机能用的?...
- UVA11192 Group Reverse【水题】
- JDBC 连接数据库,包含连接池
- 小白如何购买阿里云服务器(2019最详细教程)
- JavaScript 全栈工程师培训教程(来自阮一峰)
- postman “header“:{“retCode“:“999999“
- Java简单循环依赖的解决 —— spring_imitate(Spring的模仿)
- 网络封包编辑器mysql_WapCn网络封包编辑器
- 新浪微博Python登陆
- vagrant制作box
- 创建图层-只是保存lyr,此路不通
- html word 批注,Word2013中显示批注的两种方法
热门文章
- linux 磁盘管理 阵列,Linux 磁盘管理~~~~RAID1
- BenQ赞助2008年欧洲杯足球赛
- 扭矩大好还是马力大好_马力和扭矩到底哪个更重要?
- 用matlab画出一元二次的图,MATLAB 一元二次函数的画图.doc
- 国科大论文latex模板中可能的注意事项
- 李阳疯狂英语900句(675-900)
- 美年旅游_套餐管理_定时任务组件Quartz
- 群晖Nas通过jellyfin搭建本地影音库详细全过程(三):jellyfin之刮削小姐姐NFO
- C语言文件打开方式简介
- html文件被当毒杀掉了,文件被AVAST误杀怎么办