做一個平臺,讓對手發來一個很爛的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(三)