bitmap是什么
时序数据库支持多维查询,一般来讲,两种方式,一种是倒排索引,ES使用的;一种是bitmap索引,druid就在使用这种方式。bitmap是个什么?一种使用bit位来存储value,可以节省很大空间,在查询时也能通过与或关系快速查询。
举个例子来看,bitmap是怎么来加速多维查询的。数据如下
时间 | 城市 | 性别 | 曝光次数 |
2020-01-01 13:13:13 | CCC | gender | 1 |
2020-01-01 13:14:13 | BBB | female | 1 |
2020-01-01 13:15:13 | CCC | female | 1 |
时间是时序列,城市、性别是维度,曝光次数是指标值。这时如果有个多维查询,select 曝光 from xxx where 城市='CCC' and 性别='female'
如果不使用索引,只能全表扫描,将符合条件的取出来。如果使用bitmap可以直接取出来就是第三行,不需要扫描全表去一一比对。
bitmap的基本原理是将值映射的bit位上,再采用或/与的操作,快速定位符合条件的行,如上面两个维度可变为如下bitmap:
城市 | 性别 | ||
CCC | 101 | gender | 100 |
BBB | 010 | female | 011 |
原数据行数为3行,bitmap的大小为3,每一位代表每行的的值是否存在。所以维度值有多少个,bitmap就有多少个。比如CCC的101,代表第一行和第三行的值为CCC。所以在查询的时候,通过bimap对应的与操作,条件为 where 城市='CCC' and 性别='female',转换成bitmap计算为:
101 and 011,得到001,所以得出第三行符合条件。查到第三行后,再取出第三行对应的曝光值即可。
druid怎么构建bitmap索引
1.构建时机
druid有实时节点和历史节点,那索引的构建是在数据写入的时候还是在数据从内存持久化到硬盘的时候呢? 写入的时候构建,肯定会影响写入性能。但是批量构建的时候,实时写入到内存的那部分数据就需要另一套索引机制来进行查询,在实际查询中需要分别处理。druid采用的是后者。
2.构建维度字典
维度字典通常维护映射值,主要两个map映射,valueToId,IdToValue的映射,比如性别列,维护<gender,0>、<female,1>的一个map,再维护反过来的一个map。维度字段通常是给维度值一个自增的Int值。
3.构建bitmap
每个维度列会维护一个bitmap 数组,bitmap数组的大小对应维度值的个数。每个维度值都会对应一个bitmap。每个维度值对应的数组下标和上文的维度字典有关。比如gender的维度字段的值为0,所以的bitmap数组的下标即为0.
构建过程为:
1.为每一行生产一个自增的rowNum;
2.遍历所有的列,分别为每个维度构建相应的bitmap数组,针对每个维度的value,先在维度字典中找到对应的下标值,然后构建/找到对应下标的bitmap。找到维度值的bitmap后,再讲bitmap的下标为rowNum的bit位置为1,代表该行存在这个值。
具体过程:
1.性别维度列维护了一个bitmap数组,数组大小为2,其中下标为0对应gender的bitmap,下标为1对应female的bitmap。初始化时,两个bitmap中没有任何数字;
2.遍历第一行(rowNum为0),值为gender,根据维度字典找到对应的下标0的bitmap,然后将bitmap中下标为0的位置为1,得到;
gender:1
female:
3.遍历第二行(rowNum为1),值为female,根据维度字典找到对应下标为1的bitmap,然后将bitmap中下标为1(rowNum)的位置为1,得到:
gender:1
female:01
4.遍历第三行(rowNum为2),值为female,根据维度字段找到下标为1的bitmap,然后将bitmap中下标为2(rowNum)的位置为1,得到:
gender:1
female:011
这样就得到gender的bitmap索引,其他维度列构建的方式一样。
压缩
上文构建bitmap的过程可以看到,bitmap的长度和行数有关系,如果几十亿上百亿的数据,bitmap的长度也要这么长吗? 在实际存储是,比如性别,城市这种,会存在很多连续的值,比如刚才的例子中,第二行和第三行的值一样,是否可以采用压缩的方式来减少数据的存储? roaringbitmap比较推荐,能够压缩且对查询效率的影响不大。
bitmap如何持久化
druid的原始数据每隔一段时间就会落盘一次,原始数据落盘时,对应的bitmap索引也需要进行落盘。druid会首先将维度字典持久化到磁盘,再讲原始数据持久化为文件(原始数据对应的值已经通过维度字典替换),再讲bitmap索引持久化到文件。
druid系统是列式存储,同一个segment中所有列的数据都会分别独立存储在一起形成多个文件,比如性别列会存储在一起形成文件,曝光列会存在一起形成文件。
druid文件分为两种,一种是定长的,比如维度列是经过维度字典编码的,都是int类型的,double列,Long列都是存数值类型。一种是非定长的,比如维度字典中存储的维度值,bitmap索引中存储的bitmap数据的值也不是定长的。