Impala为什么那么快
Posted xingoo
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Impala为什么那么快相关的知识,希望对你有一定的参考价值。
导读
Impala作为CDH中通用的即席查询引擎,速度比spark、hive等要快很多,它到底做了什么能让查询变快呢?如果对这个问题感兴趣,就来看看这篇文章吧!本章中的内容都源自于impala论文,可能跟最新的版本有些脱节,但是仍然很值得参考。
一般查询引擎都会分为Frontend和Backend两部分,Frontend主要用于进行SQL的语法分析、词法分析、逻辑优化等,Backend则偏向底层做物理优化。下面就看看他们与其他查询引擎不同的地方吧
1 Frontend
Frontend主要负责解析编译SQL生成后端可以执行的查询计划。它由Java编写,由一个SQL解析器和查询优化器组成。支持标准的SQL,如select、join、groupby、orderby、limit,支持内联视图,子查询,各种join以及解析函数。
SQL的查询编译器是个标准的流程:SQL解析,语法分析,查询计划/优化。impala的查询分析器,会把标准的SQL解析成一个解析树,里面包含所有的查询信息,比如表、字段、表达式等等。一个可执行的执行计划通常包含两部分:单点的查询计划Single Node Planning 和 分布式并发查询计划 parallelization \ fragmentation。
在第一个阶段,语法解析树会被翻译成一个单点的不可以直接执行的树形结构,一般会包含:HDFS\HBase scan, hash join, cross join, union, hash agg, sort, top-n, analytic eval等。这一步主要负责基于最下层的查询节点、谓词下推、限制查询数量、join优化等优化查询性能。主要是依赖于表或者分区的统计信息进行代价评估。
第二个阶段就是基于第一个阶段优化后的单点执行计划,生成分布式的执行计划,并尽量满足最小化数据移动以及最大化数据本地性。分布式执行主要通过在节点间增加数据交换节点或者直接移动少量的数据在本地进行聚合。目前支持的join策略有broadcast和partition两种。前者是把join的整个表广播到各个节点;后者是基于hash重新分区,使得两部分数据到同一个节点。Impala通过衡量哪一种网络传输压力小,耗费的资源少,就选哪种。
所有的聚合操作都是在本地进行预聚合,然后再通过网络传输做最终的聚合。对于分组聚合,会先基于分区表达式进行的预聚合,然后通过并行的网络传输在各个节点进行每一部分的聚合。对于非分组的聚合最后一步是在单独的节点上进行。排序和TOPN的模式差不多:都是现在单独的节点进行排序/topN,然后汇总到一个节点做最后的汇总。最后根据是否需要数据交换为边界,切分整个执行计划树,相同节点的执行计划组成一个fragment。
上图展示了两个阶段的查询执行计划,左边是单个节点上的执行计划,包含了两个hdfs的scan,一个hbase的scan,后面是两个join以及聚合后的topn。
右面则是分布式切分后的执行计划图,黄色代表hdfs中的查询,绿色代表hbase查询,蓝色代表最后再coordinator上的汇总。矩形的边框则代表一个独立的fragment,箭头代表的是数据的交换。t1和t2两张表通过分区策略实现,t1和t2都会通过随机的分区值分发到不同的机器,每个机器单独进行Join。t3的join,则是通过广播实现,即把t3的数据广播到每个节点,节点单独查询。join完成后,每个节点会进行一次预聚合,然后在最后的节点上进行汇总。
2 Backend
impala的backend接收到fragment后在本地执行,它的设计采取了很多硬件上的特点,backend底层是用C++编写,使用了很多运行时代码生成的技术,对比java来说减轻内存的压力。
impala的查询设计思路也是按照volcano风格设计,处理的时候是getNext一批一批处理的。大部分的命令都是管道形式处理的,因此会消耗大量的数据存储中间数据。当执行的时候如果内存超出使用的范围,也可以把结果缓存到磁盘,经常有溢出可能的有如hash join, agg, sorting等操作。
运行时代码生成
impala内部使用了LLVM的机制,性能提升5倍。传统的编译器大概包含下面几个部分:前端、优化器、后端。
前端负责分析源码,词法分析、语法分析、语义分析,构建抽象语法树,再转成中间表示IR。中间部分针对IR再进行优化;后端则把IR转化为机器码。
LLVM是一套与编译器相关的的库,与传统的编译器不同,它更注重模块化与重用性。允许impala应用在运行时进行即时编译以及代码生成。运行时代码生成通常用于内部处理,比如用于解析文件格式的代码,对于扫描一些大表,这点尤为重要。
如果没有代码生成,那么有一些方法可能会变得很耗资源。举个例子,处理纯数字的方法肯定要比处理任意类型的方法更高效,但是在运行前,可能根本不知道这个字段是什么类型,因此生成式的方法就很有用了,可以根据底层的类型进行调优。
另外虚方法也十分耗时,如果在运行中知道具体是哪个实例,就可以直接用实例方法进行替换了。这种功能在语法表达树中很有用,在impala中,表达式可能由很多操作符组成。表达式中的每个节点都是继承了某个抽象类,执行的时候需要JVM逐层的判断虚方法,最后定位到真实的方法执行。另外有一些表达式其实很简单,比如对两个数进行相加。这样不仅减轻了虚方法的寻找时间,也增加了并行的效率,提升编译器编译效率。
3 其他
IO方面
为了基于SQL在HDFS中实现高效检索,需要利用一些HDFS的特性,如short-circuit local reads短路本地读以及Hdfs缓存。本地短路读特性可以跳过namenode,直接读取datanode本地数据,效率很高。缓存技术节省了CPU资源,无需拷贝数据块以及计算校验码。
存储方面
impala支持很多流行的存储格式,如Avro, RC,Sequence,文本,parquet等。这些文件都可以使用不同的压缩方法,如snappy, gzip, bz2等。在大多数场景推荐的都是parquet。
论文中的后半部分主要就看了这些内容,其中涉及编译器以及C++相关的内容由于经验不足,理解可能会有偏差,一切还以论文为主。
往期推荐:
xingoo
专注大数据与机器学习
以上是关于Impala为什么那么快的主要内容,如果未能解决你的问题,请参考以下文章