导读
前文 《一文读懂 SQL Server 执行计划》 中介绍过关系型数据库 SQL Server 的执行计划执行计划在数据开发过程中的重要性,以及如何阅读执行计划,根据执行计划分析 SQL 语句的执行效率问题并提出优化方案。Hive 是基于 Hadoop,实现了通过 SQL 操作 MapRedue 任务,简化了大数据编程的难度,使得普通用户也可以完成大数据程序开发。SQL 目前是使用最为广泛的结构化数据操作语言,未来大数据框架对 SQL 的支持也必将是一种趋势。Hive 在经过一系列编译过程后生成执行计划并提交 MapReduce 等执行引擎端,数据开发人员除了具备 SQL 的编程能力之外, 还必须具备 SQL 执行效率定位能力,而执行计划就是开发人员快速打开 SQL 优化大门的一把钥匙。
HQL 编译过程
本文重点不会介绍 HQL 编译过程的详细内容,对于大部分开发者来说该过程还是比较枯燥的,我们这里大概了解 Hive 会经过如下六个阶段后将 HQL 编译为物理执行计划后提交到计算引擎 MapReduce。
- 词法,语法解析
- 遍历 AST 抽象出 QB( Query Block)
- 将 QB 转化成执行操作树 OperatorTree
- 逻辑层优化器执行 OperatorTree 变换,生成逻辑执行计划,
- 遍历 OperatorTree,翻译为 MapReduce 任务,生成物理执行计划
- 物理层优化器进行 MapReduce 任务变化,最终执行计划生成
Explain 语法
Hive 提供的查看查询语句执行计划的语法如下:
EXPLAIN [EXTENDED|CBO|AST|DEPENDENCY|AUTHORIZATION|LOCKS|VECTORIZATION|ANALYZE] query
EXPLAIN :查看执行计划的基本信息;
EXPLAIN EXTENDED:加上 EXTENDED 可以输出有关计划的额外信息。这通常是物理信息,例如文件名,这些额外信息在特殊场景下可能会使用到;
EXPLAIN CBO:输出由Calcite优化器生成的计划。CBO 从 Hive 4.0.0 版本开始支持;
EXPLAIN AST:输出查询的抽象语法树。AST 在转储AST可能会导致OOM错误,因此从 EXTENDED中删除了,将在4.0.0版本中作为单独的命令使用,主要用于开发或者高级用户通过查看抽象语法树发现问题;
EXPLAIN DEPENDENCY:DEPENDENCY 在 EXPLAIN 语句中使用会产生有关计划中输入的额外信息。它显示了输入的各种属性;
EXPLAIN AUTHORIZATION:查看SQL操作相关权限的信息,从 Hive 0.14.0 开始支持;
EXPLAIN LOCKS:这对于了解系统将获得哪些锁以运行指定的查询很有用。LOCKS 从 Hive 3.2.0 开始支持;
EXPLAIN VECTORIZATION:查看SQL的向量化描述信息,显示为什么未对 Map 和 Reduce 进行矢量化。从 Hive 2.3.0 开始支持;
EXPLAIN ANALYZE:用实际的行数注释计划。从 Hive 2.2.0 开始支持;
Explain 输出
一个 HIVE 查询被转换为一个由一个或多个stage 组成的序列(有向无环图DAG)。这些 stage 可以是 Map/Reduce stage,也可以是负责元数据存储的 stage,也可以是负责文件系统的操作(比如移动和重命名)的 stage。
EXPLAIN 输出主要包括一下三部分,其中第一部分根据前文介绍在新版本中已经移除,只有其余两部分。
1), 抽象语法树 ( 该部分已经移除,使用单独的命令查看 )
2), Stage Dependencies: 各个 stage 之间的依赖性
3), Stage Plan: 各个 stage 的执行计划
按照上一部分的介绍,我们通过例子来看一下 EXPLAIN 的详细使用,查询 HQL 如下,该 SQL 是数据仓库中常见的数据仓库需求 —— 获取销售区域销售额并按照销售额按照降序排列。
SQL 脚本如下:
select ds.salesterritoryregion , sum(sales.salesamount) total_amt
from ods.dws_fact_internetsales sales
left join ods.dim_salesterritory ds on sales.salesterritorykey = ds.salesterritorykey
group by ds.salesterritoryregion
order by 2 desc;
执行计划输入结果如下,其中 第一部分 —— Stage Dependencies 共有 6 个 Stage,Stage-6 是根 stage,说明这是开始的 stage, Stag-2 依赖 Stage-6, Stage-3 依赖 Stage-2,Stage-0 依赖 Stage-2,Stage 依赖关系说明必须等待被依赖的 stage 执行结束才可以开始执行当前 stage。 一个查询任务中会有一个或者多个 Stage,每个 Stage 之间可能存在依赖关系,没有依赖关系的 stage 可以并行执行。
第二部分 —— Stage Plan ,各个 Stage 的执行计划。Stage 是 Hive 执行任务中的某一个阶段,这个阶段可能是一个 MR 任务,也可能是一个抽取任务( Fetch Operator),也可能是一个 Map Reduce Local,也可能是一个 Limit。Stage-6 是 Map Reduce Local Work,本地化的 MapReduce,意味着该表数据量比较小,Hive 选择将数据拉取到本地直接操作,没有执行分布式 MapReduce 任务。看到这里介绍一下 Fetch Operator 和 Table Scan
Fetch Operator: 客户端获取数据操作,常见属性:
1),limit,当前值是 -1,表示不限制条数,其他值为限制的条数;
TableScan: 表扫描操作, Map端的第一个操作肯定是加载表,所以就是表扫描操作。常见属性:
1),alias: 表名称
2),Statistics: 表统计信息,包含表中数据条数,数据大小等
Select Operator: 选取操作,常见属性:
1),expressions:需要的字段名称及字段类型
2),outputColumnNames:输出的列名称
3),Statistics:表统计信息,包含表中数据条数,数据大小等
HashTable Sink Operator
标志着 Hive 生成的 MapReduce 程序中 Map 阶段的结束,同时将Map端的字段组合序列化为 Reduce Key/value, Partition Key,只可能出现在Map阶段。常见属性为:
1),key:Reduce Key & Partition Key,这里是以 salesterritorykey 作为 key
Map Join Operator: Join 操作,常见属性
1),condition map:join 方式,这里是 Left Outer Join 0 to 1
2),keys: join 的条件字段,这里是 salesterritorykey
3),outputColumnNames:join 完成之后输出的字段
4),Statistics:join 完成之后生成的数据条数,大小等
Group By Operator: 分组聚合操作,常见属性:
1),aggregations:显示聚合函数信息
2),mode:聚合模式,值有 hash:随机聚合,就是hash partition;partial:局部聚合;final:最终聚合
3),keys:分组的字段,如果没有分组,则没有此字段
4),outputColumnNames:聚合之后输出列名
5),Statistics:表统计信息,包含分组聚合之后的数据条数,数据大小等
Reduce Output Operator: 输出到 Reduce 操作,常见属性:
1),sort order:值为空 不排序;值为 + 正序排序,值为 – 倒序排序;值为 +- 排序的列为两列,第一列为正序,第二列为倒序;
File Output Operator: 文件输出操作,常见的属性:
1),compressed: 是否压缩
2),table:表的信息,包含输入输出文件格式化方式,序列化方式等
Filter Operator: 过滤操作,常见属性:
1),predicate:过滤条件,SQL 语句中的过滤条件,本实例中没有使用过滤条件,所以没有出现 Filter Operator,大家可以自己尝试;
hive> explain select ds.salesterritoryregion , sum(sales.salesamount) total_amt
> from ods.dws_fact_internetsales sales
> left join ods.dim_salesterritory ds on sales.salesterritorykey = ds.salesterritorykey
> group by ds.salesterritoryregion
> order by 2 desc;
OK
STAGE DEPENDENCIES:
Stage-6 is a root stage
Stage-2 depends on stages: Stage-6
Stage-3 depends on stages: Stage-2
Stage-0 depends on stages: Stage-3
STAGE PLANS:
Stage: Stage-6
Map Reduce Local Work
Alias -> Map Local Tables:
$hdt$_1:ds
Fetch Operator
limit: -1
Alias -> Map Local Operator Tree:
$hdt$_1:ds
TableScan
alias: ds
Statistics: Num rows: 1 Data size: 4407460 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: salesterritorykey (type: int), salesterritoryregion (type: string)
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 4407460 Basic stats: COMPLETE Column stats: NONE
HashTable Sink Operator
keys:
0 _col0 (type: int)
1 _col0 (type: int)
Stage: Stage-2
Map Reduce
Map Operator Tree:
TableScan
alias: sales
Statistics: Num rows: 1 Data size: 177482752 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: salesterritorykey (type: int), salesamount (type: float)
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 177482752 Basic stats: COMPLETE Column stats: NONE
Map Join Operator
condition map:
Left Outer Join 0 to 1
keys:
0 _col0 (type: int)
1 _col0 (type: int)
outputColumnNames: _col1, _col3
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
Group By Operator
aggregations: sum(_col1)
keys: _col3 (type: string)
mode: hash
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
Reduce Output Operator
key expressions: _col0 (type: string)
sort order: +
Map-reduce partition columns: _col0 (type: string)
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
value expressions: _col1 (type: double)
Execution mode: vectorized
Local Work:
Map Reduce Local Work
Reduce Operator Tree:
Group By Operator
aggregations: sum(VALUE._col0)
keys: KEY._col0 (type: string)
mode: mergepartial
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
table:
input format: org.apache.hadoop.mapred.SequenceFileInputFormat
output format: org.apache.hadoop.hive.ql.io.HiveSequenceFileOutputFormat
serde: org.apache.hadoop.hive.serde2.lazybinary.LazyBinarySerDe
Stage: Stage-3
Map Reduce
Map Operator Tree:
TableScan
Reduce Output Operator
key expressions: _col1 (type: double)
sort order: -
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
value expressions: _col0 (type: string)
Execution mode: vectorized
Reduce Operator Tree:
Select Operator
expressions: VALUE._col0 (type: string), KEY.reducesinkkey0 (type: double)
outputColumnNames: _col0, _col1
Statistics: Num rows: 1 Data size: 195231031 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: false
Statistics: Num rows: 1 Data size: 195231031 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
Time taken: 0.208 seconds, Fetched: 103 row(s)
Hive 优化
到目前, Hive 执行计划中使用到的所有操作符基本都有介绍到,通过对以上执行计划的解析,我们大概可以理解 Hive 是如何转换为 Map Reduce 任务的,以及每种 SQL 语句对应哪一种 Operator。因此 Hive 的调优最后还是需要对 MapReduce 的工作原理了解, Hive SQL 的调优本质还是 MapReduce 作业的优化。 Hive 中没有关系型数据库 ( 如 SQL Server 的执行计划缓存等等机制 ),Hive 作业中主要是对 HDFS 磁盘文件的读取与计算。所以对 Hive 作业的调优基本有以下几个原则:
1),尽量使用表分区
2),尽量在读取数据的时候过滤不必要的数据
3),关联表的时候不管关联多少表,尽量使用同列关联,这样可以在一个 Map 任务搞定,如果不是同一列,则会新开一个 MapReduce 任务;
4),避免空值的影响,如果关联业务主键上存在大量的 null 值,则会有 shuffle 产生,进而引起数据倾斜。
5),尽可能在开发阶段避免数据倾斜的发生,当数据倾斜发生时可以考虑通过 MapJoin 提前在 Map 阶段完成 join 操作,避免不必要的 shuffle 阶段,从而提高资源利用率。 MapJoin 一般适用于小表关联大表的场景,不适用于大表和大表的关联。
6),对于大数据量业务关联键值确实分布不均的情况,可以通过对关联主键首先进行膨胀,借助随机数的方式,将引发数据倾斜的数据进行分散到不同的 Reduce 端提高处理效率。
Explain Dependency
explain denpendency 用于描述查询 SQL 需要的数据来源,输出是以 JSON 格式的数据,包含如下两个部分内容:
1),input_tables:用于描述 SQL 依赖的数据来源表,其中 tablename ( 表名 )以及 tabletype( 表类型 );
hive> explain dependency select ds.salesterritoryregion , sum(sales.salesamount) total_amt
from ods.dws_fact_internetsales sales
left join ods.dim_salesterritory ds on sales.salesterritorykey = ds.salesterritorykey
group by ds.salesterritoryregion
order by 2 desc;
OK
{"input_tables":[{"tablename":"[email protected]_fact_internetsales","tabletype":"MANAGED_TABLE"},{"tablename":"[email protected]_salesterritory","tabletype":"MANAGED_TABLE"}],"input_partitions":[]}
Time taken: 0.223 seconds, Fetched: 1 row(s)
2),input_partitions:用于描述 SQL 依赖的数据来源表,其中 tablename ( 表名 ), tabletype( 表类型 )以及依赖的表分区信息,详细内容看如下 SQL 示例。
hive> explain dependency select shipdate , sum(sales.salesamount) total_amt
from dwh.dws_fact_internetsales sales
where sales.shipdate >= '2014/02/04 00:00:00.000000000'
group by shipdate;
OK
{"input_tables":[{"tablename":"[email protected]_fact_internetsales","tabletype":"MANAGED_TABLE"}],"input_partitions":[{"partitionName":"[email protected][email protected]=2014%2F02%2F04 00%3A00%3A00.000000000"}]}
Time taken: 0.174 seconds, Fetched: 1 row(s)
结尾
除了以上开发过程中最常使用的 Explain 命令外, Explain 还提供了 Authorization,CBO,Locks 等等命令,Explain 确实大数据 Hive 开发过程中比不可少的工具,与 SQL Server 中的执行计划功能相当,还需要我们仔细研究学习,提高数据开发的开发效率,程序的健壮性。我们只是对 Hive 中 Explain 做了一个简单的学习与了解以及对 Hive 优化的初步了解,不足与错误之处,还请见谅,还需要在开发过程中进一步实践,加深对 Hive 以及 MapReduce 的理解。
往期文章
数据仓库系列
Hive 系列
SQL Server 优化
本文由mdnice多平台发布
原创文章,作者:ItWorker,如若转载,请注明出处:https://blog.ytso.com/280440.html