Impala是一个MPPMassivelyParallelProcessing计算引擎,简单来说就是将计算压力分到多个节点,得到结果后汇总,然后再返回给客户端。如果你留意过Impala的执行计划,会观察到exchange
节点,该节点的作用就是分散计算压力的过程,impala
的架构相关,可点击关于MySQL,PostgreSQL,Impala,Spark的执行计划
下面我们通过sql
语句 + 截图的方式熟悉下impala
的执行计划
查看impala
的执行情况有3种途径, 1️⃣ execution plan
在SQL
没有实际运行之前就可以获取;2️⃣ profile
SQL
实际运行之后搜集的执行信息;3️⃣ summary
也是SQL
实际执行之后搜集的信息相比于profile
更加详细
本文将重点探讨第一种execution plan
的执行
测试表表结构及数据状况如下
drop table if exists test.user;
create table if not exists test.user as
select 1 as user_id , 'z1' as user_name union all
select 2 as user_id , 'z2' as user_name union all
select 3 as user_id , 'z3' as user_name
;
drop table if exists test.goods;
create table if not exists test.goods as
select '001' as goods_id , '手机' as goods_name union all
select '002' as goods_id , '洗衣机' as goods_name union all
select '003' as goods_id , '冰箱' as goods_name ;
;
drop table if exists test.order_tb;
create table if not exists test.order_tb as
select 1 as order_no , 1 as user_id , '001' as goods_id union all
select 2 as order_no , 1 as user_id , '002' as goods_id union all
select 3 as order_no , 2 as user_id , '002' as goods_id
;
简单的select *
操作,exechange中不需要partitioned,且没有搜集表的统计信息,基数cardinality信息不可用unaviable
左边的逻辑是执行表扫描之后,进行一次预聚合然后按照user_id的哈希值分发,相同user_id去往同一个节点之后,merge结果;右侧和左侧的区别在于执行了compuate status
之后,统计到数据都在一个节点,所以只需要单节点内的聚合
左图:首先节点内排序,排序完成后所有节点归并外排即归并排序1。右图中搜集了目标表的统计信息,只需要在单节点内执行排序操作
左图中首先按照partition by
分区字段exechange
往不同的节点,然后是排序user_id正序null值排在队首,然后是执行窗口操作,可以观察到实际使用的是rows between unbounded preceding and current row
队首到当前行,最后exechange
之后返回结果,相比于order by
的排序的区别在于没有最终归并排序的步骤。右图的区别在于搜集到目标表的统计信息后,所有操作在单节点内完成
左图中:扫描t2表后广播t2表,关联方式为hash join
,关联完成后扫描t3表,广播t3后,和t1关联,关联方式为hash join
,最后exechange
返回结果。右图中搜集到了各个表的统计信息后,t1和t2关联,之后t3 right join
t1表。二者使用的方式都是Hash Join
,关于Hash Join
可以参考这篇文章单机与分布式下的 Join 是怎么玩的?
在没有收集表信统计信息的情况下,Impala
不会知道谁是大表谁是小表,也就无法完成类似Hive优化器那样,总是用小表驱动大表的功能
上面描述的表数据量很小,是小表之间的关联,下面描述的是大表之间的关联及其执行计划
dwd.live_order_basic_di
,dwd.live_order_goods_di
亿级记录的的大表dim.live_zhubo_user_info_df
小表(千条记录)️左图是大表关联小表,可以认为是广播哈希关联
exechange
的方式是广播hash join
anchor_id
执行exechange
merge
多个节点的结果exechange
给协调器QueryCoordinator️右图是大表关联大表,可以认为是混洗哈希关联
exechange
exechange
(经过步骤1和步骤2之后,相同的关联键都去往同一个节点)hash join
exechange
(相同的分组键去往相同的节点)merge
多个节点的结果exechange
给协调器QueryCoordinator下图是两种关联方式的区别:
上图左图和右图展示了,如果group by A
字段,distinct B
字段的实际效果就是先group by A, B
字段,然后在按照其中一个字段group by,然后执行聚合函数
如上图所示,如果对多个字段执行distinct
,将会有多个分组calss_x同时进行,这对impalad
的压力是非常大的,生产环境中多字段distict
非常容易把impalad
玩坏
compute stats tb_name
时候,整体脚本更慢?情况1:执行explain
的时候即便没有表的统计信息也会生成一个执行计划,按照该执行计划执行得到耗时为 t i m e _ A time\_A time_A,
情况2:假设执行compute stats tb_name
的耗时为 t i m e _ B time\_B time_B,但是在执行explain
之后得到了和情况1一样的执行计划,此时整体消耗的时间为: t i m e _ A + t i m e _ B time\_A + time\_B time_A+time_B
♂ 所以一个建议是在涉及大表的查询/关联时,不建议优先执行 compute stats tb_name
Impala在没有收集统计信息的前提下,执行关联有如下逻辑
1️⃣ t1 left join t2
,exchange
(哈希或者广播) t2表
2️⃣ t1 right join t2
, exchange
(哈希或者广播) t1表
projection投影 pushdown | predicate谓词 pushdown | runtime filter |
---|---|---|
列过滤 | 行过滤(更底层的过滤方式) | 行过滤(运行加载到内存过滤) |
关于predicate pushdow和runtime filter的区别
This optimization is called filter pushdown or predicate pushdown and aims at pushing down the filtering to the “bare metal”, i.e. a data source engine. That is to increase the performance of queries since the filtering is performed at the very low level rather than dealing with the entire dataset after it has been loaded to Spark’s memory and perhaps causing memory issues.
由于谓词下推predicatePushdown是一种更底层的过滤方式,因此比较于运行时过滤runtimeFilter具备更高的效率
本文首发于公众号stackoverflow,欢迎关注
Reference
几乎无一例外的,只要涉及到大规模数据的排序,都需要使用归并排序,因为归并排序的优势在于可以突破内存的限制,而且是一个 O ( n ∗ l o g n ) O(n*log^n) O(n∗logn)的时间复杂度的排序方式 ↩︎