第一節 參考
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表結構類似,但是新版被禁用,不能查詢.
第五節 Sql解析
PhoenixPreparedStatement.executeQuery()->PhoenixStatement.executeQuery(),核心處理都在這個方法裡面。
一.調用ExecutableSelectStatement.compilePlan(PhoenixStatement, ValueOp)解析sql語句.在這個方法裡面.
1.調用SubselectRewriter.flatten(SelectStatement, PhoenixConnection)重寫子查詢.
2.調用FromCompiler.getResolverForQuery()擷取查詢的列解析器.如下圖:
如果是單表查詢,建立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端