文章做者:吴建超程序员
内容来源:jackywoo.cn数据库
导读:Kylin、Druid、ClickHouse是目前主流的OLAP引擎,本文尝试从数据模型和索引结构两个角度,分析这几个引擎的核心技术,并作简单对比。在阅读本文以前但愿能对Kylin、Druid、ClickHouse有所理解。微信
01
Kylin数据模型
Kylin的数据模型本质上是将二维表(Hive表)转换为Cube,而后将Cube存储到HBase表中,也就是两次转换。数据结构
第一次转换,其实就是传统数据库的Cube化,Cube由CuboId组成,下图每一个节点都被称为一个CuboId,CuboId表示固定列的数据数据集合,好比“ AB” 两个维度组成的CuboId的数据集合等价于如下SQL的数据集合:架构
select A, B, sum(M), sum(N) from table group by A, B
第二次转换,是将Cube中的数据存储到HBase中,转换的时候CuboId和维度信息序列化到rowkey,度量列组成列簇。在转换的时候数据进行了预聚合。下图展现了Cube数据在HBase中的存储方式。大数据
02
Kylin索引结构
由于Kylin将数据存储到HBase中,因此kylin的数据索引就是HBase的索引。HBase的索引是简化版本的B+树,相比于B+树,HFile没有对数据文件的更新操做。ui
HFile的索引是按照rowkey排序的聚簇索引,索引树通常为二层或者三层,索引节点比MySQL的B+树大,默认是64KB。数据查找的时候经过树形结构定位到节点,节点内部数据是按照rowkey有序的,能够经过二分查找快速定位到目标。编码
Kylin小结:适用于聚合查询场景;由于数据预聚合,Kylin能够说是最快的查询引擎(group-by查询这样的复杂查询,可能只须要扫描1条数据);kylin查询效率取决因而否命中CuboId,查询波动较大;HBase索引有点相似MySQL中的联合索引,维度在rowkey中的排序和查询维度组合对查询效率影响巨大;因此Kylin建表须要业务专家参与。spa
03
Druid数据模型
Druid数据模型比较简单,它将数据进行预聚合,只不过预聚合的方式与Kylin不一样,kylin是Cube化,Druid的预聚合方式是将全部维度进行Group-by,能够参考下图:.net
04
Druid索引结构
Druid索引结构使用自定义的数据结构,总体上它是一种列式存储结构,每一个列独立一个逻辑文件(其实是一个物理文件,在物理文件内部标记了每一个列的start和offset)。对于维度列设计了索引,它的索引以Bitmap为核心。下图为“city”列的索引结构:
首先将该列全部的惟一值排序,并生成一个字典,而后对于每一个惟一值生成一个Bitmap,Bitmap的长度为数据集的总行数,每一个bit表明对应的行的数据是不是该值。Bitmap的下标位置和行号是一一对应的,因此能够定位到度量列,Bitmap能够说是反向索引。同时数据结构中保留了字典编码后的全部列值,其为正向的索引。
那么查询如何使用索引呢?以如下查询为例:
select site, sum(pv) from xx where date=2020-01-01 and city='bj' group by site
-
city列中二分查找dictionary并找到'bj'对应的bitmap
-
遍历city列,对于每个字典值对应的bitmap与'bj'的bitmap作与操做
-
每一个相与后的bitmap即为city='bj'查询条件下的site的一个group的pv的索引
-
经过索引在pv列中查找到相应的行,并作agg
-
后续计算
Druid小结:Druid适用于聚合查询场景可是不适合有超高基维度的场景;存储全维度group-by后的数据,至关于只存储了KYLIN Cube的 Base-CuboID;每一个维度都有建立索引,因此每一个查询都很快,而且没有相似KYLIN的巨大的查询效率波动。
05
ClickHouse索引结构(只讨论MergeTree引擎)
由于Clickhouse数据模型就是普通二维表,这里不作介绍,只讨论索引结构。总体上Clickhouse的索引也是列式索引结构,每一个列一个文件。Clickhouse索引的大体思路是:首先选取部分列做为索引列,整个数据文件的数据按照索引列有序,这点相似MySQL的联合索引;其次将排序后的数据每隔8194行选取出一行,记录其索引值和序号,注意这里的序号不是行号,序号是从零开始并递增的,Clickhouse中序号被称做Mark’s number;而后对于每一个列(索引列和非索引列),记录Mark’s number与对应行的数据的offset。
下图中以一个二维表(date, city, action)为例介绍了整个索引结构,其中(date,city)是索引列。
那么查询如何使用索引呢?以如下查询为例:
select count(distinct action) where date=toDate(2020-01-01) and city=’bj’
-
二分查找primary.idx并找到对应的mark's number集合(即数据block集合)
-
在上一步骤中的 block中,在date和city列中查找对应的值的行号集合,并作交集,确认行号集合
-
将行号转换为mark's number 和 offset in block(注意这里的offset以行为单位而不是byte)
-
在action列中,根据mark's number和.mark文件确认数据block在bin文件中的offset,而后根据offset in block定位到具体的列值。
-
后续计算
该实例中包含了对于列的正反两个方向的查找过程。反向:查找date=toDate(2020-01-01) and city=’bj’数据的行号;正向:根据行号查找action列的值。对于反向查找,只有在查找条件匹配最左前缀的时候,才能剪枝掉大量数据,其它时候并不高效。
Clickhouse小结:MergeTree Family做为主要引擎系列,其中包含适合明细数据的场景和适合聚合数据的场景;Clickhouse的索引有点相似MySQL的联合索引,当查询前缀元组能命中的时候效率最高,但是一旦不能命中,几乎会扫描整个表,效率波动巨大;因此建表须要业务专家,这一点跟kylin相似。
06
小结
-
Kylin、Druid只适合聚合场景,ClickHouse适合明细和聚合场景
-
聚合场景,查询效率排序:Kylin > Druid > ClickHouse
-
Kylin、ClickHouse建表都须要业务专家参与
-
Kylin、ClickHouse查询效率均可能产生巨大差别
-
ClickHouse在向量化方面作得的最好,Druid少许算子支持向量化、Kylin目前还不支持向量化计算。
今天的分享就到这里,谢谢你们。
做者介绍:
吴建超,9年程序员一枚,目前专一于大数据处理和数据库技术。
在文末分享、点赞、在看,给个三连击呗~~
有热门推荐👇
本文分享自微信公众号 - 一万小时极客(coding-Hub)。
若有侵权,请联系 support@oschina.cn 删除。
本文参与“OSC源创计划”,欢迎正在阅读的你也加入,一块儿分享。