参考资料
由于经常有Impala SQL将一台或多台 Impalad 所在的机器的内存消耗殆尽,而通常的解决方案是直接取消该SQL查询。我一直以来总想优雅地去处理这些SQL,比如挂个钩子给客户端返回一些执行计划信息,告知如何优化 SQL,或者直接给他调整优化重新提交SQL等等。
那如何从一堆profile信息中提取关键信息是我首先要解决的。
所以,我花了一些时间仔细地研究了Impala 的 执行计划(plan)及概要(profile)。重点参考了油管上这个专题内容:How to use Impala’s query plan and profile to fix Performance
英文比较好的小伙伴完全可以跳过我的文章直接进行学习。
一、Impala SQL执行流程及架构
首先先看
1. SQL执行流程。
Impalad 分为Java前端与C++处理后端,接受客户端连接的Impalad即作为这次查询的Coordinator,Coordinator通过JNI调用Java前端对用户的查询SQL进行分析生成执行计划树。
Java前端产生的执行计划树以 Thrift 数据格式返回给 Impala C++后端(Coordinator),由 Coordinator 根据执行计划,通过调度器对生成的执行计划树分配给相应的后端执行器 Impalad 执行,并把结果通过网络流式的传送回给Coordinator,由Coordinator 返回给客户端。
2. Impala 的逻辑结构:
这边是以 CDH6.3.2 为例,CDP 权限验证使用的是Ranger。
总体可分为元数据(控制层)、执行层、存储层。
StateStore:检查集群中所有 Impalad 的运行状况,并不断将其结果转发给每个 Impalad,同时持续将元数据广播给协调器。
Catalog:缓存元数据,包含 HDFS 元数据和数据库、表字段等元数据,以及与安全相关的信息、授权信息等。一旦 Catalog 加载了这些元数据,就会将他们缓存,并发送给 StateStore,然后分发给 Impalad。
Impalad:接受客户端查询,解析查询请求,编译生成并行任务,然后分发任务到其他 Impalad 并行执行,并返回结果。
每个Impalad都可以既做 Coordinator 又做 Executor,他们分别是上面第一张图中说的 Impalad 的前端和后端。(但其实一般会配置专用 Coordinator 角色)
二、什么是 Impala查询计划和概要
1. 查询计划(Plan)
Impala 的执行计划是Impala根据客户端提交的 SQL 生成的一系列操作节点图,不同的操作对应不同的 PlanNode, 如:ScanNode、SortNode、 AggregationNode, HashJoinNode、LimitNode、UnionNode、ExchangeNode 等等。Impala生成执行计划的主要目的如下:
- 通过分区裁剪、谓词下推、运行时过滤器等方法,尽可能减少不必要的工作
- 使用 DN 块元数据最大化扫描位置。
- 最小化数据移动(优化连接顺序,选择更好的连接策略)
- 将任务并行化,在多个节点上运行,以缩短执行时间。
2. 查询概要(Profile)
Impala Profile是Impala在执行SQL查询时生成的一种详细报告,它提供了查询执行过程中的各种参数和统计信息,帮助用户理解查询的执行过程、识别性能瓶颈并进行优化。
三、在哪看执行计划和概要
1. CM查询历史页:
点击查询详细信息
2. Impala WebUI:
只能在 Impala Coordinator上可以查看:
这边有 plan 拓扑图,颜色越暖,表示消耗的资源越多。
3. Impala-shell
通过 explain命令可以查询 plan,执行结束后通过 profile查询详细的执行概要。
explain有三种级别,1-3,其中 1 表示最低级别,输出的执行计划比较简单,3表示最高级别,输出的执行计划最详细。
set explain_level=1;
[node2:21000] testdb> set explain_level=1;
EXPLAIN_LEVEL set to 1
[node2:21000] testdb> explain select c.c_name,sum(o.o_totalprice*l.l_quantity)
from customer c
join orders o on c.c_custkey=o.o_custkey
join lineitem l on l.l_orderkey = o.o_orderkey
group by c.c_name;
Query: explain select c.c_name,sum(o.o_totalprice*l.l_quantity)
from customer c
join orders o on c.c_custkey=o.o_custkey
join lineitem l on l.l_orderkey = o.o_orderkey
group by c.c_name
+------------------------------------------------------------------------------------+
| Explain String |
+------------------------------------------------------------------------------------+
| Max Per-Host Resource Reservation: Memory=139.02MB Threads=8 |
| Per-Host Resource Estimates: Memory=4.43GB |
| WARNING: The following tables are missing relevant table and/or column statistics. |
| testdb.customer, testdb.lineitem, testdb.orders |
| |
| PLAN-ROOT SINK |
| | |
| 10:EXCHANGE [UNPARTITIONED] |
| | |
| 09:AGGREGATE [FINALIZE] |
| | output: sum:merge(o.o_totalprice * l.l_quantity) |
| | group by: c.c_name |
| | row-size=27B cardinality=unavailable |
| | |
| 08:EXCHANGE [HASH(c.c_name)] |
| | |
| 05:AGGREGATE [STREAMING] |
| | output: sum(o.o_totalprice * l.l_quantity) |
| | group by: c.c_name |
| | row-size=27B cardinality=unavailable |
| | |
| 04:HASH JOIN [INNER JOIN, BROADCAST] |
| | hash predicates: o.o_orderkey = l.l_orderkey |
| | runtime filters: RF000 <- l.l_orderkey |
| | row-size=67B cardinality=unavailable |
| | |
| |--07:EXCHANGE [BROADCAST] |
| | | |
| | 02:SCAN HDFS [testdb.lineitem l] |
| | partitions=1/1 files=8 size=48B |
| | row-size=15B cardinality=unavailable |
| | |
| 03:HASH JOIN [INNER JOIN, BROADCAST] |
| | hash predicates: c.c_custkey = o.o_custkey |
| | runtime filters: RF002 <- o.o_custkey |
| | row-size=52B cardinality=unavailable |
| | |
| |--06:EXCHANGE [BROADCAST] |
| | | |
| | 01:SCAN HDFS [testdb.orders o] |
| | partitions=1/1 files=8 size=263B |
| | runtime filters: RF000 -> o.o_orderkey |
| | row-size=30B cardinality=unavailable |
| | |
| 00:SCAN HDFS [testdb.customer c] |
| partitions=1/1 files=5 size=99B |
| runtime filters: RF002 -> c.c_custkey |
| row-size=22B cardinality=unavailable |
+------------------------------------------------------------------------------------+
set explain_level=3;
[node2:21000] testdb> set explain_level=3;
EXPLAIN_LEVEL set to 3
[node2:21000] testdb> explain select c.c_name,sum(o.o_totalprice*l.l_quantity)
from customer c
join orders o on c.c_custkey=o.o_custkey
join lineitem l on l.l_orderkey = o.o_orderkey
group by c.c_name;
Query: explain select c.c_name,sum(o.o_totalprice*l.l_