概述
可能所有的 SQLboy 刚接触 SQL 语句的时候都是 select xxx from xxx where xxx。在 hive 中,我们把这种语句称为 select-from-where 型语句,也可称为简单 SQL,这类简单 SQL 是特指不含有变转换函数,聚合函数,开窗函数和连接操作的 SQL 语句。
这类 SQL 主要特征是只有 map 阶段,没有 reduce 阶段。
本文分析一下这类简单 SQL 执行计划和性能,让我们从最基础的 SQL 分析,hive 简单语句 select from where 型语句性能分析,逐渐深入,进而学会分析复杂 SQL 的性能和执行计划。
所有的复杂 SQL(几百行?上千行?)都是由一个个简单 SQL 带一些特殊函数堆叠而成的。
1.不带函数操作的 select-from-where 型简单 SQL
这类 SQL 语句通常只有 select-from-where,没有其他函数操作,或者操作符处理,例如字符串截取。
1.1 执行示例
例 1 不带函数操作的 select-from-where 型简单 SQL。
-- 本文默认使用mr计算引擎
explain
-- 统计年龄等于30岁的所有昵称
select age,nick from temp.user_info_all
where ymd = '20230505'
and age = 30;
复制代码
执行执行计划结果:
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: user_info_all
Statistics: Num rows: 32634295 Data size: 783223080 Basic stats: COMPLETE Column stats: NONE
Filter Operator
predicate: (age = 30) (type: boolean)
Statistics: Num rows: 16317147 Data size: 391611528 Basic stats: COMPLETE Column stats: NONE
Select Operator
expressions: 30 (type: bigint), nick (type: string)
outputColumnNames: _col0, _col1
Statistics: Num rows: 16317147 Data size: 391611528 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: true
Statistics: Num rows: 16317147 Data size: 391611528 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
复制代码
通过以上内容,我们可以看到,整个 SQL 逻辑执行过程中只有 map 操作树(Map Operate Tree),若转换成 MapReduce 来看的话,即只有 Map 阶段的任务。
1.2 运行逻辑分析
以上流程我们可以分解为运行逻辑图来看,如下图:
我们在之前的文章中提起过,Hive 执行计划是一个预估的执行计划,只有在 SQL 实际执行后才会获取到真正的执行计划。那我们来看看以上语句的实际运行控制台打印过程。额,失算了,因为结果太多,限制一下输出条数。
Query ID = hdfs_20230613111158_03c8f6e1-e04f-4e4e-aa9b-569a89860438
Total jobs = 1
Launching Job 1 out of 1
# 这里表示没有reduce任务,reduce任务执行的服务器节点是0个。
Number of reduce tasks is set to 0 since there's no reduce operator
...
Hadoop job information for Stage-1: number of mappers: 6; number of reducers: 0
2023-06-13 11:12:28,564 Stage-1 map = 0%, reduce = 0%
2023-06-13 11:12:45,219 Stage-1 map = 17%, reduce = 0%, Cumulative CPU 6.17 sec
...
2023-06-13 11:12:54,523 Stage-1 map = 100%, reduce = 0%, Cumulative CPU 40.52 sec
MapReduce Total cumulative CPU time: 40 seconds 520 msec
Ended Job = job_1675664438694_14052273
MapReduce Jobs Launched:
Stage-Stage-1: Map: 6 Cumulative CPU: 40.52 sec HDFS Read: 203436481 HDFS Write: 2412 SUCCESS
Total MapReduce CPU Time Spent: 40 seconds 520 msec
复制代码
从上面的结果可以知道,实际的运行过程也是只有 map 阶段的操作。
针对 select-from-where 只有 map 阶段操作而没有 reduce 阶段的主要原因是这类 SQL 只有从表中读取数据并执行数据行的过滤,并没有需要将 HDFS 在其他节点上的数据与该节点数据放在一起处理的必要,因此这类 SQL 不需要 reduce 操作。Map 阶段过滤后的数据,就是最终的结果数据。
这种只含 map 的操作,如果文件大小控制在合适的情况下,都将只有本地操作,其执行非常高效,运行效率完全不输于在计算引擎 Tez 和 Spark 上运行。感兴趣的小伙伴可以去将三者运行效率比对一下。
1.3 伪代码解释
接下来我们再以 mr 伪代码的方式理解一下上述语句的运行情况:
例 2 MRselect-from-where 简单 SQL 代码解析
map(inkey,invalue,context);
colsArray = invalue.split("\t");
//对应filter操作,过滤掉age=30的数据行,ymd为分区列,属于文件级操作,这里不展示了。
if int(colsArray[11]) == 30 {
//获取age,nick两列,就是投影操作,即select操作
age = colsArray[11];
nick = colsArray[7];
//最后输出两列age,nick,执行计划中对应的为_col0和_col1.这里invalue为1
context.write(age,nick);
}
reduce(inkey,invalue,context)
//pass表示不会执行
pass;
复制代码
2.带普通函数和运行操作符的普通型 SQL 执行计划解读
这里的普通函数特指除表转换函数(UDTF),聚合函数和窗口函数之外的函数。例如:nvl(),cast(),case when,concat(),year()等,具体有哪些,后续会专门罗列。
这类 SQL 也属于 select-from-where 型 SQL,其主要特点也是只有 map 阶段处理。
我们也可以给它更具体的称为 select-function(column)-from-where-function(column)类。
2.1 执行计划解读
接下来可以看一个带普通函数和操作符的 SQL 执行计划案例。
例 3 带普通函数和操作符的 SQL 运行计划。
explain
-- 统计年龄等于30岁的所有昵称
select uid,
nvl(client,'android') as client,
case when age > 20 then '老腊肉' else '小鲜肉' end as label,
concat(nick,'_测试') as nick,
cast(chat_uv as double)/10 as chat
from temp.user_info_all
where ymd = '20230505'
and age in (18,19,20,21) and chat_uv is not null and substr(uid,0,1) = '1';
复制代码
输出的执行计划结果:
STAGE DEPENDENCIES:
Stage-1 is a root stage
Stage-0 depends on stages: Stage-1
STAGE PLANS:
Stage: Stage-1
Map Reduce
Map Operator Tree:
TableScan
alias: user_info_all
Statistics: Num rows: 32634295 Data size: 783223080 Basic stats: COMPLETE Column stats: NONE
# where 条件过滤
Filter Operator
predicate: ((age) IN (18, 19, 20, 21) and chat_uv is not null and (substr(uid, 0, 1) = '1')) (type: boolean)
Statistics: Num rows: 8158574 Data size: 195805776 Basic stats: COMPLETE Column stats: NONE
# 列投影
Select Operator
expressions: uid (type: bigint), NVL(client,'android') (type: string), CASE WHEN ((age > 20)) THEN ('老腊肉') ELSE ('小鲜肉') END (type: string), concat(nick, '_测试') (type: string), (UDFToDouble(chat_uv) / 10) (type: double)
outputColumnNames: _col0, _col1, _col2, _col3, _col4
Statistics: Num rows: 8158574 Data size: 195805776 Basic stats: COMPLETE Column stats: NONE
File Output Operator
compressed: true
Statistics: Num rows: 8158574 Data size: 195805776 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
复制代码
通过以上执行计划我们可以看到,这个结果同 select-from-where 型 SQL,只有 map 阶段的操作,如果实际去运行以上任务,得到的执行步骤也和例 1 类似。即在 map 运行完整个作业任务结束。
结合以上实例我们可以得出一个结论 select-function(colums)-from-where-function(column)这种类型的 SQL 可以归于 select-from-where 类简单 SQL 类型。
2.2 伪代码解释逻辑
例 4 例 2 的 MapReduce 伪代码执行逻辑。
//整个程序只有map阶段,没有reduce逻辑
map(inkey,invalue,context);
//数据输入是一行数据
colsArray = invalue.split("\t");
if age in (18,19,20,21) and chat_uv != null and substr(uid, 0, 1) == '1'{
uid = colsArray[0];
client = colsArray[3];
if client == null{
client = 'android';
}
label = '';
if age > 20 {
label = '老腊肉';
} else {
label = '小鲜肉';
}
nick = nick+'_测试');
chat = double(chat_uv)/10;
}
context.write(uid,client+'\t'+label+'\t'+nick+'\t'+chat);
复制代码
文章转载自:鲁边
原文链接:https://www.cnblogs.com/lubians/p/17478285.html
评论