Hive執行計劃之只有map階段SQL性能分析和解讀

目錄

概述

可能所有的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,隨機贈送一本魯邊備註版珍藏大數據書籍。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章