Hive Explain 语句类似Mysql 的Explain 语句,提供了对应查询的执行计划,对于我们在理解Hive底层逻辑、Hive调优、Hive SQL书写等方面提供了一个参照,在我们的生产工作了是一个很有意义的工具。
1.Hive Explain语法
EXPLAIN [EXTENDED|CBO|AST|DEPENDENCY|AUTHORIZATION|LOCKS|VECTORIZATION|ANALYZE] query
hive explain 的语法规则如上,后面将按照对应的子句进行探讨。
1.1 基本语法: Explain HiveSQL
EXTENDED 语句会在执行计划中产生关于算子(Operator)的额外信息,这些信息都是典型的物理信息,如文件名称等。
在执行Explain QUERY 之后,一个查询会被转化为包含多个Stage的语句(看起来更像一个DAG)。这些Stages要么是map/reduce Stage,要么是做些元数据或文件系统操作的Stage (如 move 、rename等)。Explain的输出包含2个部分:
- 执行计划不同Stage之间的以来关系(Dependency)
- 每个Stage的执行描述信息(Description)
以下将通过一个简单的例子进行解释。
执行Explain 语句:
EXPLAIN
SELECT SUM(id) FROM test1;
Explain输出结果解析:
- 依赖图
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
这里显示有3个Stage, stage-1是根阶段,stage-2依赖stage-1,stage-0依赖stage-2
- 各Stage的计划
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: test1
Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: id (type: int)
outputColumnNames: id
Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
Group By Operator
aggregations: sum(id)
mode: hash
outputColumnNames: _col0
Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator
sort order:
Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
value expressions: _col0 (type: bigint)
Reduce Operator Tree:
Group By Operator
aggregations: sum(VALUE._col0)
mode: mergepartial
outputColumnNames: _col0
Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 1 Data size: 8 Basic stats: COMPLETE Column stats: NONE
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
Stage: Stage-0
Fetch Operator
limit: -1
Processor Tree:
ListSink
STAGE PLAN 中包含2个MapReduce Stage (stage-1和stage-0)。
每个Mapreduce Stage本身包含两个部分:
- Map Operator Tree:map端的执行计划树
- Reduce Operator Tree的: reduce端的执行计划树
这两个执行计划树里边,包含这条SQL的operator:
- map端第一个操作肯定是加载表,所以就是 TableScan 表扫描操作,常见的属性:
- alias: 表名称
- Statistics: 表统计信息,包含表中数据条数,数据大小等
- Select Operator: 选取操作,常见的属性 :
- expressions:需要的字段名称及字段类型
- outputColumnNames:输出的列名称
- Statistics:表统计信息,包含表中数据条数,数据大小等
- Group By Operator:分组聚合操作,常见的属性:
- aggregations:显示聚合函数信息
- mode:聚合模式,值有 hash:随机聚合,就是hash partition;partial:局部聚合;final:最终聚合
- keys:分组的字段,如果没有分组,则没有此字段
- outputColumnNames:聚合之后输出列名
- Statistics: 表统计信息,包含分组聚合之后的数据条数,数据大小等
- Reduce Output Operator:输出到reduce操作,常见属性:
- sort order:值为空 不排序;值为 + 正序排序,值为 - 倒序排序;值为 ± 排序的列为两列,第一列为正序,第二列为倒序
- Filter Operator:过滤操作,常见的属性:
- predicate:过滤条件,如sql语句中的where id>=1,则此处显示(id >= 1)
- Map Join Operator:join 操作,常见的属性:
- condition map:join方式 ,如Inner Join 0 to 1 Left Outer Join0 to 2
- keys: join 的条件字段
- outputColumnNames: join 完成之后输出的字段
- Statistics: join 完成之后生成的数据条数,大小等
- File Output Operator:文件输出操作,常见的属性
- compressed:是否压缩
- table:表的信息,包含输入输出文件格式化方式,序列化方式等
- Fetch Operator 客户端获取数据操作,常见的属性:
- limit,值为 -1 表示不限制条数,其他值为限制的条数
根据上面的Stage执行计划:
在Stage-1阶段执行了Map Container,其中的操作顺序:TableScan (表文件扫描)→ Select(字段筛选) → Groupby (map端预聚合) → ReduceOutput (预聚合输出到临时文件)
在Stage-0阶段执行了Reduce Container,其中的操作顺序:Groupby (对Map端输出进行全局聚合) → FileOutput (文件输出到HDFS目录)
1.2 语句:EXPLAIN DEPENDENCY HiveSQL
该子句会展示执行计划中输入源相关的额外信息。
执行以下语句:
EXPLAIN DEPENDENCY
SELECT
COUNT(*) cnt
FROM testdb.test3 a
LEFT JOIN
testdb.test4 b
ON a.id=b.id
WHERE a.name LIKE 'jack%'
;
输出结果:
{"input_partitions":[{"partitionName":"testdb@test3@date_id=20200101"},{"partitionName":"testdb@test3@date_id=20200102"},{"partitionName":"testdb@test3@date_id=20200103"},{"partitionName":"testdb@test3@date_id=20200104"},{"partitionName":"testdb@test3@date_id=20200105"},{"partitionName":"testdb@test3@date_id=20200106"},{"partitionName":"testdb@test3@date_id=20200107"},{"partitionName":"testdb@test4@date_id=20200101"},{"partitionName":"testdb@test4@date_id=20200102"},{"partitionName":"testdb@test4@date_id=20200103"},{"partitionName":"testdb@test4@date_id=20200105"},{"partitionName":"testdb@test4@date_id=20200107"}],"input_tables":[{"tablename":"testdb@test3","tabletype":"MANAGED_TABLE"},{"tablename":"testdb@test4","tabletype":"MANAGED_TABLE"}]}
上面的输出结果,显示了执行的SQL依赖的底层数据,以一个大JSON的方式展示处理,其中包含了input_tables 和 input_partitions两个字段,分别底层扫描的表和对应扫描的分区(如果表是分区表)
1.3 语句:EXPLAIN AUTHORIZATION HiveSQL
该子句会展示执行查询的所有需要被授权的实体,若存在授权失败的情况也会展示。
执行下边的语句:
EXPLAIN AUTHORIZATION
SELECT * FROM src JOIN srcpart;
输出结果:
INPUTS:
default@srcpart
default@src
default@srcpart@ds=2008-04-08/hr=11
default@srcpart@ds=2008-04-08/hr=12
default@srcpart@ds=2008-04-09/hr=11
default@srcpart@ds=2008-04-09/hr=12
OUTPUTS:
hdfs://localhost:9000/tmp/.../-mr-10000
CURRENT_USER:
navis
OPERATION:
QUERY
AUTHORIZATION_FAILURES:
Permission denied: Principal [name=navis, type=USER] does not have following privileges for operation QUERY [[SELECT] on Object [type=TABLE_OR_VIEW, name=default.src], [SELECT] on Object [type=TABLE_OR_VIEW, name=default.srcpart]]
结果中展示了输入、输出的对应文件目录,以及用户、操作类型的情况,特别注意对应授权失败的情况,如果没有对应表的相关权限,会被体现出来。
1.4 其他语句
EXPLAIN CBO/AST/LOCKS/VECTORIZATION/ANALYZE 要在Hive 2.0以后才可用,本次暂不详解
2.Hive Explain 总结
通过对Hive Explain 的学习,我们能在日常的问题排查、性能调优、底层原理等方面更加深入。那么,可以根据其他网友以及自我总结,在以下几个点学到一些东西。
2.1.join 会过滤null值?
//执行下面语句:
explain select [a.id](http://a.id/),b.user_name from test1 a join test2 b on [a.id=b.id](http://a.id%3Db.id/);
//输出结果
TableScan
alias: a
Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
Filter Operator
predicate: id is not null (type: boolean) --判断:id is not null,进行了过滤
Statistics: Num rows: 6 Data size: 75 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: id (type: int)
......
2.2 group by会进行排序吗?
//执行语句:
explain select id,max(user_name) from test1 group by id;
//输出结果:
TableScan
alias: test1
Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: id (type: int), user_name (type: string)
outputColumnNames: id, user_name
Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
Group By Operator
aggregations: max(user_name)
keys: id (type: int)
mode: hash
outputColumnNames: _col0, _col1
Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator
key expressions: _col0 (type: int)
sort order: + --排序:根据上面的key 排序,默认正序
Map-reduce partition columns: _col0 (type: int)
Statistics: Num rows: 9 Data size: 108 Basic stats: COMPLETE Column stats: NONE
value expressions: _col1 (type: string)
......
2.3 子查询过滤和join后过滤性能有差别吗?
//执行SQL1
explain
SELECT
a.id,
b.user_name
FROM
test1 a
JOIN test2 b ON a.id = b.id
WHERE
a.id > 2;
//执行SQL2
explain
SELECT
a.id,
b.user_name
FROM
(SELECT * FROM test1 WHERE id > 2) a
JOIN test2 b ON a.id = b.id;
//会发现两个语句的执行计划几乎一样,都是TableScan 然后where过滤,后进行join
2.4 Hive Explain 展示的HiveSQL 各个子句执行顺序?
//执行如下查询的Explain(查询涉及了where、join、on等场景)
EXPLAIN
select
a.f_time,a.f_merchant_id,b.create_time,b.agent_class
from dw.dwd_t_agent_profit_statement_card_di a
left join dw.dim_agent_f b
on (a.f_agent_id=b.agent_id and b.agent_level=1 )
where a.date_id>='20210101'
;
具体执行计划如下:
STAGE DEPENDENCIES:
Stage-4 is a root stage , consists of Stage-1
Stage-1
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-4
Conditional Operator
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: a
filterExpr: (date_id >= '20210101') (type: boolean)
Statistics: Num rows: 200284367 Data size: 32253640262 Basic stats: PARTIAL Column stats: NONE
Reduce Output Operator
key expressions: f_agent_id (type: string)
sort order: +
Map-reduce partition columns: f_agent_id (type: string)
Statistics: Num rows: 200284367 Data size: 32253640262 Basic stats: PARTIAL Column stats: NONE
value expressions: f_merchant_id (type: string), f_time (type: timestamp)
TableScan
alias: b
filterExpr: (agent_level = 1) (type: boolean)
Statistics: Num rows: 990700 Data size: 346454976 Basic stats: COMPLETE Column stats: COMPLETE
Filter Operator
predicate: (agent_level = 1) (type: boolean)
Statistics: Num rows: 61918 Data size: 8606602 Basic stats: COMPLETE Column stats: COMPLETE
Reduce Output Operator
key expressions: agent_id (type: string)
sort order: +
Map-reduce partition columns: agent_id (type: string)
Statistics: Num rows: 61918 Data size: 8606602 Basic stats: COMPLETE Column stats: COMPLETE
value expressions: create_time (type: timestamp), agent_class (type: tinyint)
Reduce Operator Tree:
Join Operator
condition map:
Left Outer Join0 to 1
keys:
0 f_agent_id (type: string)
1 agent_id (type: string)
outputColumnNames: _col4, _col40, _col90, _col128
Statistics: Num rows: 220312808 Data size: 35479005057 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: _col40 (type: timestamp), _col4 (type: string), _col90 (type: timestamp), _col128 (type: tinyint)
outputColumnNames: _col0, _col1, _col2, _col3
Statistics: Num rows: 220312808 Data size: 35479005057 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 220312808 Data size: 35479005057 Basic stats: COMPLETE Column stats: NONE
table:
input format: org.apache.hadoop.mapred.TextInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
serde: org.apache.hadoop.hive.serde2.lazy.LazySimpleSerDe
Stage: Stage-0
Fetch Operator
limit: -1
Processor Tree:
ListSink
具体分析如下:
依赖关系:该语句被划分为stage-4、stage-1、stage-0三个阶段,stage-4与stage-1被优化为一个stage执行。
Stage-4: 一个条件操作(被优化放入stage-1中)
Stage-1:
Map操作树:
对应两张表的扫描:
表a为分区表,表b为非分区表。
SQL中在where条件对a进行了条件筛选,在on关联中对b进行了筛选;
对a表选取了f_merchant_id和f_time字段,对b表读取了。
(因此:1.在sql中分区表进行分区筛选很有必要 2.子查询中尽可能早的筛选需要的字段,减少IO 3.On中进行条件筛选时要注意join类型,inner join 时On条件与where条件效果一样,left/right join时对主表On条件与where条件效果一样,left/right join时对副表On条件与where条件效果不一样)
Reduce操作树:
先进行join,关联条件 0 f_agent_id 与 1 agent_id,输出读取的字段
再进行select操作,按select排列顺序输出字段
最后进行文件输出操作,数据数据到对应的表(分区)目录或临时文件目录
stage-0:
进行Fetch操作,由于没有写入操作,所以直接进行fetch并展示
那么,可以归纳下hive SQL的执行顺序:
1.先执行from tbl_name 和where 子句,进行表扫描,筛选对应数据
2.执行join 和 on 子句,join过程中进行on 匹配
3.执行select操作,获取需要的字段
4.进行group by 操作(如果有groupby),进行分组
5.进行聚合操作(如果有,如sum/count/max/min等),进行聚合运算
6.进行聚合赛选操作(如果有,having 筛选语句),进行聚合后的聚合结果筛选
7.进行order by操作(如果有),进行排序
8.进行limit操作(如果有,limit),进行限制输出