1. 程式人生 > 實用技巧 >做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(二)

做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(二)

上一篇:做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(一)

因為主要想借助hive的思路來實現對sql的優化,所以這一篇主要是梳理一條sql在hive內部大概是什麼樣的生命週期

首先通過一張圖看下,內部sql大概執行流:

sql經過一系列的規則處理後,最後變成task tree,然後mapreduce通過task tree來執行job

接下來通過原始碼,看下是如何處理的!

另外我把編譯好的hive(1.2.1版本)和hadoop(2.7.0版本)程式碼放在Git上,這樣感興趣的同學直接下載下來,就可以在本地debug跑

Hive編譯後的原始碼:https://github.com/
niutaofan/apache-hive-1.2.1-src.git
hadoop編譯後的原始碼:連結:https://pan.baidu.com/s/1meF9MFHUAyY1Mk7mMOdqIg 密碼:fwnh

1、大體流程

1)、Driver.compile 接收SQL , 然後通過:pd.parse(command)將SQL轉換為ASTNode(這個過程包含了詞法解析和語法解析)

  1.1、ParseDriver.parse接收到sql語句,然後通過:r = parser.statement();解析了詞法和語法

  1.2、拿到解析後的HiveParser.statement_return,然後通過ASTNode tree = (ASTNode) r.getTree();獲取到ASTNode

2)、通過sem.analyze(tree, ctx);從AST Node到Phsical Optimize這幾個階段,都是在SemanticAnalyzer.analyzeInternal()方法中進行的(語義解析、生成邏輯執行計劃、優化邏輯執行計劃等)

  2.1、拿到ASTNode之後,通過SemanticAnalyzer.analyzeInternal()進行優化;

  2.2、程式碼會排程到CalcitePlanner.analyzeInternal (這個方法內部會做 一個流程的判斷:if (runCBO) 是否執行CBO優化),當然不管執行RBO還是CBO,最後呼叫的都是:SemanticAnalyzer.analyzeInternal()

  2.3、在SemanticAnalyzer.analyzeInternal()方法中,首先基於ASTNode做了各種規則優化,根據需求包括了籠統的:RBO和CBO的優化,最終返回Operator

    在Hive中,使用Calcite來進行核心優化,它將AST Node轉換成QB,又將QB轉換成Calcite的RelNode,在Calcite優化完成後,又會將RelNode轉換成Operator Tree,說起來很簡單,但這又是一條很長的呼叫鏈。

    Calcite優化的主要類是CalcitePlanner,更加細節點,是在CalcitePlannerAction.apply()這個方法,CalcitePlannerAction是一個內部類,包括將QB轉換成RelNode,優化具體操作都是在這個方法中進行的。

  

2、一條sql的原始碼之路

如果想debug的方式走讀原始碼,那麼需要如下幾個步驟:

第一步:啟動本地的hadoop原始碼(NameNode和DataNode)

第二步:啟動hive的metastore服務

第三步:啟動(Debug方式)CliDriver類

根據上文提示, sql在客戶端執行後,會在Driver.compile 接收SQL , 然後通過:pd.parse(command)將SQL轉換為ASTNode(這個過程包含了詞法解析和語法解析)

eg. 執行一段sql(sql的資料,提前放入hive了) , 看下hive是如何解析和優化的

select 
  * 
from 
  (
    select 
      Sname, 
      Sex, 
      Sage, 
      Sdept, 
      count(1) as num 
    from 
      student_ext 
    group by 
      Sname, 
      Sex, 
      Sage, 
      Sdept
  ) t1 
where 
  Sage > 10;

Driver.compile程式碼:

上圖比較重要的點:

ParseDriver

Hive使用的是antlr來做詞法、語法的解析工作,最終生成一棵有語義的ast樹
而在Hive中呼叫antlr類的程式碼org.apache.hadoop.hive.ql.parse.ParseDriver類,通過ParseDriver.parse 可以返回HiveParser.statement_return 

而這個HiveParser.statement_return通過強轉,即可拿到ASTNode,如下圖:

######################################################思考############################################################################################

如果需求是快速實現對使用者輸入的sql進行詞法和語法解析,以便達到自定義或者sql優化的需求,那麼可不可以利用上述內容進行重構???

答案是肯定可以的,而且非常簡單,只需要知道,hive在做sql的詞法和語法解析,使用的是哪個包(org.apache.hadoop.hive.ql.parse)

然後開啟一個新的工程,匯入hive-exec包即可

第一步:maven匯入依賴

 <dependency>
            <groupId>org.apache.hive</groupId>
            <artifactId>hive-exec-nt</artifactId>
            <version>1.2.1</version>
 </dependency>
 <dependency>
            <groupId>org.apache.hadoop</groupId>
            <artifactId>hadoop-common</artifactId>
            <version>2.7.1</version>
 </dependency>

第二步:程式碼編寫

import org.apache.hadoop.hive.ql.parse.ASTNode;
import org.apache.hadoop.hive.ql.parse.ParseDriver;
import org.apache.hadoop.hive.ql.parse.ParseException;
/**
 * Created by niutao
 */
public class Tests {
    public static void main(String[] args) {
        String sql = "SELECT `object_id`, `column1_id`, COUNT(DISTINCT `cookie`) AS `COOKIE`\n" +
                "FROM `D_DSJ_INDEX_PDS`.`INDEX2_FLW_COOKIE_INTEREST_OBJECT_D_FACT`\n" +
                "WHERE `dim_day` >= '2020-03-03' AND `dim_day` <= '2020-03-16' AND `series_id` = '692'\n" +
                "GROUP BY `object_id`, `column1_id`\n" +
                "ORDER BY COUNT(DISTINCT `cookie`) IS NULL DESC, COUNT(DISTINCT `cookie`) DESC\n" +
                "LIMIT 200";
        //1、匯入模仿hive,匯入ParseDriver
        ParseDriver pd = new ParseDriver();
        //2、解析sql
        try {
            ASTNode ast = pd.parse(sql);
            //3、測試,列印解析樹
            System.out.println(ast.dump());
        } catch (ParseException e) {
            e.printStackTrace();
        }
    }
}

列印 結果:

通過以上方式,即可將sql解析出ASTNode

######################################################################################################################################################

接著之前的原始碼,看下在生成ASTNode之後 , 是如何根據ASTNode來做優化的;

請檢視下一篇:做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(三)