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

2023-06-13 18:00:22

目錄

概述

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