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表结构类似,但是新版被禁用,不能查询.

第五节 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端