天天看點

phoenix 源碼分析v0.01

第一節 參考

https://blog.csdn.net/gaoshui87/article/details/52180414

問題:

1.phoenix生成scan,最後發給server執行的代碼沒找到

2.local index的執行過程  

3.phoenix得server端協處理器如何處理的 

4.ConnectionImplementation.locateRegionInMeta() 擷取regionServer位置的邏輯.發送給hbase前有沒有給regisonserver,region,

hfile,rowkey的具體資訊?

第二節 架構

第三節 啟動

第四節 Sql執行流程

以單表查詢使用了索引為例.

一.用戶端定位region

1.root表在zk的位置 get /hbase/meta-region-server.可以使用hbase指令zk_dump查詢.

2.meta表的region位置使用指令scan 'hbase:meta'查詢。如下圖.meta和root表結構類似,但是新版被禁用,不能查詢.

phoenix 源碼分析v0.01
phoenix 源碼分析v0.01

第五節 Sql解析

PhoenixPreparedStatement.executeQuery()->PhoenixStatement.executeQuery(),核心處理都在這個方法裡面。

一.調用ExecutableSelectStatement.compilePlan(PhoenixStatement, ValueOp)解析sql語句.在這個方法裡面.

1.調用SubselectRewriter.flatten(SelectStatement, PhoenixConnection)重寫子查詢.

2.調用FromCompiler.getResolverForQuery()擷取查詢的列解析器.如下圖:

phoenix 源碼分析v0.01

如果是單表查詢,建立SingleTableColumnResolver對象.如果是多表join,建立MultiTableColumnResolver對象.

(1).單表查詢邏輯->進入BaseColumnResolver.createTableRef()打開業務表.->MetaDataClient.updateCache()去SYSTEM:CATALOG表緩存裡面查詢業務表的列資訊->ConnectionQueryServicesImpl.getTable()先計算tablekey作為start rowkey,然後->ConnectionQueryServicesImpl.metaDataCoprocessorExec()->HTable.coprocessorService()調用meta資料的協處理器擷取CATALOG的start key,end key ,region資訊,然後連接配接協處理器的channel,查詢SYSTEM:CATALOG表的資料,發RPC請求到server端.->ConnectionImplementation.locateRegion().定位catalog表的region.在前面業務代碼調用getConnection()時已經擷取了該表位置。這裡是從緩存取.->在updateCache()中擷取到業務表的PTable後,把該業務表資訊加入緩存.

(2).多表join邏輯.->MultiTableColumnResolver.visit(),先後擷取左子樹表和右子樹表的region表資訊->BaseColumnResolver.createTableRef().以兩表join為例,依次擷取左表和右表的資訊放到MultiTableColumnResolver.tableMap中.

3.調用StatementNormalizer.normalize(SelectStatement, ColumnResolver)重寫查詢,建立SelectStatement.SelectStatement()對象,構造文法樹.如果是單表,處理常量.->ParseNodeRewriter.rewrite().解析select,where子句.如果是多表join,依次解析映射的各個列.

進入ParseNodeRewriter.rewrite().

4.調用SubqueryRewriter.transform()重寫子查詢.

5.建立compile.QueryCompiler,調用QueryCompiler.compile()生成查詢計劃.在調用QueryCompiler的構造函數中,new hbase.client.Scan,hbase的Scan對象.在QueryCompiler.compile()中,建立StatementContext.StatementContext(),如果是單表查詢,進入QueryCompiler.compileJoinQuery()。如果是多表join,調用JoinCompiler.compile()建立JoinTable,進入QueryCompiler.compileJoinQuery()。

(1).JoinCompiler.compile()邏輯:建立JoinCompiler,判斷是否啟用USE_SORT_MERGE_JOIN.取出join的左右表建立JoinTable。在JoinSpec的list中指定join關系.

(2).QueryCompiler.compileJoinQuery()邏輯:如果沒有指定SortMergeJoin,預設join清單為HASH_BUILD_RIGHT, HASH_BUILD_LEFT.調用QueryCompiler.compileJoinQuery()按照前面的join順序,switch生成查詢計劃傳回。以from wg inner join  wj 為例,預設是HASH_BUILD_RIGHT,以wg為主表。->建立TupleProjector處理映射的列.->從 List<JoinSpec>依次取出join的表,這裡隻有wj.->建立子掃描hbase.client.Scan對象,這個是hbase針對join的表的掃描對象.->建立子掃描的StatementContext,調用QueryCompiler.compileJoinQuery()建立子掃描的查詢計劃,基本是遞歸進入5.子掃描進入QueryCompiler.compileSingleFlatQuery()建立單表的查詢計劃.這就是用explain時join有兩個掃描計劃.調用JoinCompiler.joinProjectedTables()建立join的PTableImpl.->調用JoinSpec.compileJoinConditions()處理join的條件.把條件拆分成左右表的單獨列.->調用HashJoinPlan.create()建立HashJoinPlan。

第六節 sql優化

在PhoenixStatement.executeQuery()->ExecutableSelectStatement.compilePlan中調用QueryOptimizer.optimize()進行sql優化.

一.單表查詢

1.如果是單表查詢,調用QueryOptimizer.getApplicablePlansForSingleFlatQuery().解析業務表的所有列.處理查詢中的hint注解.周遊所有可以走的索引,根據索引調用QueryOptimizer.addPlan()重新生成查詢計劃.

2.QueryOptimizer.addPlan().

(1).在FromCompiler.getResolverForQuery()重新建立列解析器.

(2).調用ParseNodeRewriter.rewrite(SelectStatement, ParseNodeRewriter)重寫查詢.

(3).調用QueryCompiler.compileSelect(SelectStatement)生成查詢計劃.->TupleProjectionCompiler.ColumnRefVisitor.visit()解析業務表的列資訊.

二.多表查詢.

1.調用JoinCompiler.compile()建立JoinTable.建立主表查詢的SQL語句select x from wg where wg.xxx=.xxx是join的條件.建立這條sql的單表查詢計劃,優化查詢計劃.然後建立join表的sql語句,查詢計劃,優化查詢計劃.

第七節 sql執行處理器

sql執行分兩步,第一步是在PhoenixStatement.executeQuery中修改Scan對象。第二部是執行ResultSet.next()一條條查詢資料庫記錄.

一.PhoenixStatement.executeQuery()->BaseQueryPlan.iterator()修改Scan對象。

1.設定Scan對象的scn,timeRange,tenantId,localIndex.設定scan中select時映射的所有列.

2.調用ScanPlan.newIterator()建立ParallelIterators.ParallelIterators()并行掃描疊代器.在這個疊代器的構造函數BaseResultIterators.BaseResultIterators()中調用BaseResultIterators.getParallelScans()建立掃描Hbase的Scan對象,這個是個核心方法.

3.BaseResultIterators.getParallelScans邏輯.

(1).調用BaseResultIterators.toBoundaries()确定region查詢的邊界.

(2).如果使用localIndex,調用BaseResultIterators.computePrefixScanRanges()計算邊界.

(3)設定屬性BaseResultIterators.scan,這個屬性就是HBase的Scan對象hbase.client.Scan.

二.周遊結果集

1.調用PhoenixResultSet.next().進入RoundRobinResultIterator.next().->BaseResultIterators.getIterators().->BaseResultIterators.getIterators().->ParallelIterators.submitWork()。在submitWork()中,周遊所有的Scan,通過future,異步調用server端,發出RPC請求.

2.異步future邏輯.LookAheadResultIterator.peek()->TableResultIterator.next()

(1).先調用TableResultIterator.initScanner()中建立ScanningResultIterator對象TableResultIterator.scanIterator.這個對象中包含了HBase的hbase.client.Scan掃描對象.

(2).調用ScanningResultIterator.next()調用hbase.client.ResultScanner.next()進入hbase的jar包查詢下一條記錄.如下圖:

後續邏輯參考<hbase源碼分析> 第四節 用戶端.

三.HashSubPlan.execute(HashJoinPlan)連接配接的執行過程.執行單表wj的scan過程.掃描出所有資料?因為explain時這張表是全表掃描,資料較少.

每條資料都去查詢了region的位置.這段代碼沒太看懂??

第八節. Server端

繼續閱讀