Hive SQL解析/執行計劃生成流程分析

Hive SQL解析/執行計劃生成流程分析

Hive有三種用戶接口:

cli (Command line interface) bin/hive或bin/hive –service cli 命令行方式(默認)
hive-server/hive-server2 bin/hive –service hiveserver 或bin/hive –service hiveserver2 通過JDBC/ODBC和Thrift訪問(Impala通過這種方式借用hive-metastore)
hwi (Hive web interface) bin/hive –service hwi 通過瀏覽器訪問

在hive shell中輸入“show tables;”實際執行的是:

1
bin/hadoop jar hive/lib/hive-cli-0.9.0.jar org.apache.hadoop.hive.cli.CliDriver -e 'SHOW TABLES;'

CLI入口函數:cli.CliDriver.main()

讀入參數->建立SessionState並導入配置->處理輸入文件中指令CliDriver.processFile();或交互型指令CliDriver.processLine()->解析輸入CliDriver.processCmd()

(1) 如果是quit或者exit,退出

(2) 以source開頭的,讀取外部文件並執行文件中的HiveQL

(3) !開頭的命令,執行操作系統命令(如!ls,列出當前目錄的文件信息)

(4) list,列出jar/file/archive

(5) 其他命令,則生成調用相應的CommandProcessor處理,進入CliDriver.processLocalCmd()

下面看看CliDriver.processLocalCmd()這個函數:

set/dfs/add/delete指令交給指定的CommandProcessor處理,其餘的交給org.apache.hadoop.hive.ql.Driver.run()處理

org.apache.hadoop.hive.ql.Driver類是查詢的起點,run()方法會先後調用compile()和execute()兩個函數來完成查詢,所以一個command的查詢分爲compile和execute兩個階段。

Compile

(1)利用antlr生成的HiveLexer.java和HiveParser.java類,將HiveQL轉換成抽象語法樹(AST)。

首先使用antlr工具將srcqlsrcjavaorgapachehadoophiveqlparsehive.g編譯成以下幾個文件:HiveParser.java, Hive.tokens, Hive__.g, HiveLexer.java

HiveLexer.java和HiveParser.java分別是詞法和語法分析類文件,Hive__.g是HiveLexer.java對應的詞法分析規範,Hive.tokens定義了詞法分析後所有的token。

然後沿着“Driver.compile()->ParseDriver.parse(command, ctx)->HiveParserX.statement()->antlr中的API”這個調用關係把輸入的HiveQL轉化成 ASTNode類型的語法樹。HiveParserX是由antlr生成的HiveParser類的子類。

(2)利用對應的SemanticAnalyzer類,將AST樹轉換成Map-reduce task。主要分爲三個步驟:

a) AST -> operator DAG

b) optimize operator DAG

c) oprator DAG -> Map-reduce task

首先接着上一步生成的語法樹ASTNode, SemanticAnalyzerFactory會根據ASTNode的token類型生成不同的 SemanticAnalyzer (所有這些SemanticAnalyzer都繼承自BaseSemanticAnalyzer)

1) ExplainSemanticAnalyzer

2) LoadSemanticAnalyzer

3) ExportSemanticAnalyzer

4) DDLSemanticAnalyzer

5) FunctionSemanticAnalyzer

6) SemanticAnalyzer

然後調用BaseSemanticAnalyzer.analyze()->BaseSemanticAnalyzer. analyzeInternal()。

下面以最常見的select * from table類型的查詢爲例,進入的子類是SemanticAnalyzer. analyzeInternal(),這個函數的邏輯如下:

1) doPhase1():將sql語句中涉及到的各種信息存儲起來,存到QB中去,留着後面用。

2) getMetaData():獲取元數據信息,主要是sql中涉及到的 表 和 元數據 的關聯

3) genPlan():生成operator tree/DAG

4) optimize:優化,對operator tree/DAG 進行一些優化操作,例如列剪枝等(目前只能做rule-based optimize,不能做cost-based optimize)

5) genMapRedTasks():將operator tree/DAG 通過一定的規則生成若干相互依賴的MR任務

Execute

將Compile階段生成的task信息序列化到plan.xml,然後啓動map-reduce,在configure時反序列化plan.xml

實例分析:

在hive中有這樣一張表:

uid

fruit_name

count

a

apple

5

a

orange

3

a

apple

2

b

banana

1

執行如下的查詢:

1
SELECT uid, SUM(countFROM logs GROUP BY uid

通過explain命令可以查看執行計劃:

1
EXPLAIN SELECT uid, SUM(countFROM logs GROUP BY uid;

依照hive.g的語法規則,生成AST如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
ABSTRACT SYNTAX TREE:
(
  TOK_QUERY
  (TOK_FROM (TOK_TABREF (TOK_TABNAME logs)))
  (
    TOK_INSERT
    (TOK_DESTINATION (TOK_DIR TOK_TMP_FILE))
    (
      TOK_SELECT
      (TOK_SELEXPR (TOK_TABLE_OR_COL uid))
      (TOK_SELEXPR (TOK_FUNCTION sum (TOK_TABLE_OR_COL count)))
    )
    (TOK_GROUPBY (TOK_TABLE_OR_COL uid))
  )
)

生成的執行計劃operator tree/DAG如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
STAGE DEPENDENCIES:
  Stage-1 is a root stage
  Stage-0 is a root stage
STAGE PLANS:
  Stage: Stage-1
    Map Reduce
      Alias -> Map Operator Tree:
        logs
          TableScan // 掃描表
            alias: logs
            Select Operator //選擇字段
              expressions:
                    expr: uid
                    type: string
                    expr: count
                    type: int
              outputColumnNames: uid, count
              Group By Operator //在map端先做一次聚合,減少shuffle數據量
                aggregations:
                      expr: sum(count) //聚合函數
                bucketGroup: false
                keys:
                      expr: uid
                      type: string
                mode: hash
                outputColumnNames: _col0, _col1
                Reduce Output Operator //輸出key,value給reduce
                  key expressions:
                        expr: _col0
                        type: string
                  sort order: +
                  Map-reduce partition columns:
                        expr: _col0
                        type: string
                  tag: -1
                  value expressions:
                        expr: _col1
                        type: bigint
      Reduce Operator Tree:
        Group By Operator
          aggregations:
                expr: sum(VALUE._col0) //聚合
          bucketGroup: false
          keys:
                expr: KEY._col0
                type: string
          mode: mergepartial
          outputColumnNames: _col0, _col1
          Select Operator //選擇字段
            expressions:
                  expr: _col0
                  type: string
                  expr: _col1
                  type: bigint
            outputColumnNames: _col0, _col1
            File Output Operator //輸出到文件
              compressed: false
              GlobalTableId: 0
              table:
                  input format: org.apache.hadoop.mapred.TextInputFormat
                  output format: org.apache.hadoop.hive.ql.io.HiveIgnoreKeyTextOutputFormat
  Stage: Stage-0
    Fetch Operator
      limit: -1

Hive優化策略:

1. 去除查詢中不需要的column

2. Where條件判斷等在TableScan階段就進行過濾

3. 利用Partition信息,只讀取符合條件的Partition

4. Map端join,以大表作驅動,小表載入所有mapper內存中

5. 調整Join順序,確保以大表作爲驅動表

6. 對於數據分佈不均衡的表Group by時,爲避免數據集中到少數的reducer上,分成兩個map-reduce階段。第一個階段先用Distinct列進行shuffle,然後在 reduce端部分聚合,減小數據規模,第二個map-reduce階段再按group-by列聚合。

7. 在map端用hash進行部分聚合,減小reduce端數據處理規模。

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