這種只含map的操作,如果文件大小控制在合適的情況下,都將只有本地操作,其執行非常高效,運行效率完全不輸於在計算引擎Tez和Spark上運行。 ...
目錄
目錄概述
可能所有的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階段的任務。
如果有執行計劃里關鍵詞不熟悉的,建議閱讀這篇 Hive執行計劃之一文讀懂Hive執行計劃 。
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);
下一期:Hive常見時間函數的使用與問題整理
按例,歡迎點擊此處關註我的個人公眾號,交流更多知識。
後臺回覆關鍵字 hive,隨機贈送一本魯邊備註版珍藏大數據書籍。