Fourousky

  博客园  :: 首页  :: 新随笔  :: 联系 :: 订阅 订阅  :: 管理

常见OLAP引擎对比

常见OLAP引擎包括不仅限于Hive、Spark SQL、Presto、Kylin、Impala、Druid、Clickhouse、Greeplum,目前业务系统刚用ClickHouse代替原有的MPP查询,使用效率提高了40%
与OLTP不同,OLAP更强调SQL的执行速度,分区,强调磁盘I/O,OLTP强调事务,强调并发,强调内存效率以及命中率
OLAP目前开源的很多,但是没有一种能完全解决所有场景,没有一个系统能同时在数据量、性能、和灵活性三个方面做到完美,每个系统在设计时都需要在这三者间做出取舍,在实际使用过程中,由于各OLAP底层实现不同,在SQL上也有差异,所以切换业务系统的OLAP,其成本也将引起很多问题,在业务考虑上最好能考虑一种完全推进下去,目前较为主流并且应用于大型系统上的有CH,Kylin,Druid本文详细介绍这三种,顺带介绍其余的Spark SQL等

ClickHouse

索引结构

Clickhouse 数据模型就是普通二维表,只讨论索引结构。整体上 Clickhouse 的索引也是列式索引结构,每个列一个文件。Clickhouse 索引的大致思路是:首先选取部分列作为索引列,整个数据文件的数据按照索引列有序,这点类似 MySQL 的联合索引;其次将排序后的数据每隔 8192 行选取出一行,记录其索引值和序号,注意这里的序号不是行号,序号是从零开始并递增的,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引擎众多,最常用并且默认的引擎是Merge Tree引擎,其分布式引擎在测试上面能提高更为复杂SQL的查询速度,但是其分布式表是依赖于ZK的伪分布式,需要专门维护本地表做分布式表
  • MergeTree Family 作为主要引擎系列,其中包含适合明细数据的场景和适合聚合数据的场景;
  • Clickhouse 的索引有点类似 MySQL 的联合索引,当查询前缀元组能命中的时候效率最高,可是一旦不能命中,几乎会扫描整个表,效率波动巨大;所以建表需要业务专家,这一点跟 kylin 类似。

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 中的存储方式。

索引结构

Kylin 将数据存储到 HBase 中,所以 kylin 的数据索引就是 HBase 的索引。HBase 的索引是简化版本的 B+树,相比于 B+树,HFile 没有对数据文件的更新操作。
HFile 的索引是按照 rowkey 排序的聚簇索引,索引树一般为二层或者三层,索引节点比 MySQL 的 B+树大,默认是 64KB。数据查找的时候通过树形结构定位到节点,节点内部数据是按照 rowkey 有序的,可以通过二分查找快速定位到目标。

Kylin 小结

  • 适用于聚合查询场景;因为数据预聚合,Kylin 可以说是最快的查询引擎(group-by 查询这样的复杂查询,可能只需要扫描 1 条数据);
  • kylin 查询效率取决于是否命中 CuboId,查询波动较大;HBase 索引有点类似 MySQL 中的联合索引,维度在 rowkey 中的排序和查询维度组合对查询效率影响巨大;所以 Kylin 建表需要业务专家参与
  • kylin较为适合老业务系统,目前很多传统系统都是采用Hive,Hbase 来做数仓,其切换成本很多,采用Kylin技术栈方面能较为合理,并且由于其依赖于Hadoop生态,和Hive能完美结合
  • kylin目前支持实时+离线一站式解决方案,并且引入Spark来替换原有的MR构建Cube,构建时间大大缩短
  • 对运维和业务要求较高,业务上需要提前购建好维度,跟查询效率直接相关,并且运维也有较高的要求,首先必须了解HBase,Hive,MapReduce,Spark,HDFS,Yarn的原理;其次对MapReduce Job和Spark Job的问题排查和调优经验要丰富;然后必须掌握对Cube复杂调优的方法;最后出现问题时排查的链路较长,复杂度较高

实际使用场景:
链家:Apache Kylin 实践:链家数据分析引擎的演变史

Druid

数据模型

Druid 数据模型比较简单,它将数据进行预聚合,只不过预聚合的方式与 Kylin 不同,kylin 是 Cube 化,Druid 的预聚合方式是将所有维度进行 Group-by,可以参考下图:

索引结构

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 的巨大的查询效率波动。

有赞:Druid在有赞的实践

posted on 2021-05-02 10:53  Fourousky  阅读(2479)  评论(0编辑  收藏  举报