因為主要想借助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,如下圖:
ps:這塊兒存在極大的性能問題,后續會提到並改進
######################################################思考############################################################################################
如果需求是快速實現對用戶輸入的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來做優化的;
請查看下一篇:基於calcite做傻瓜式的sql優化(三)