在做TPC-C on phoenix的时候遇到了一个SQL优化的问题,主要表现为phoenix的join没有考虑where子句,见PHOENIX-3310。
例子
看下面两条语句和他们的执行计划:
|
|
其中where子句里的col1是两张表的primary key。但是join compiler得到的plan还是要做full scan。
调用过程
通常我们通过jdbc连接phoenix然后执行SQL的代码会这么写
|
|
这些都是通过java标准的jdbc操作的,getConnection()
这里不进行追踪,主要看PreparedStatement
的executeQuery
方法。
首先createStatement()
返回的是PhoenixPreparedStatement
对象,它实现了java.sql.PreparedStatement
,继承自PhoenixStatement
。调用executeQuery()
方法,调用PhoenixStatement
的executeQuery()
,下面主要看包含join的select where执行计划生成过程。executeQuery()
的主要代码为:
|
|
整个过程分为3步,根据statement进行compile得到plan,然后optimize plan,最后根据不同的plan获取iterator,iterator是一层层加上去的,最终得到resultset。我们主要关注前两步。
compile
跟踪上面代码第一行的compilePlan
,它是CompilableStatement
接口定义的,根据SQL这里的stmt
应该是ExecutableSelectStatement
,于是查看ExecutableSelectStatement
的compilePlan
方法。
|
|
前面是做一个检查,看有没有UDF,然后重写statement,后面3行是关键,通过new一个QueryCompiler
,调用其compile()
方法得到plan,进入compile()
方法。compile()
-> compileSelect()
。
|
|
从上面可以看到,首先是通过JoinCompiler.optimize
进行了优化,然后compileJoinQuery()
。JoinCompiler.optimize
下部分优化时再详细看,先接着走。
compileJoinQuery()
这部分很长,最终是通过转换然后compileSingleFlatQuery()
,把groupby,orderby,where等合起来得到plan。
optimize
从plan = connection.getQueryServices().getOptimizer().optimize(PhoenixStatement.this, plan)
的optimze()
开始跟踪
optimize()
->getApplicablePlans()
-> orderPlansBestToWorst()
这些都是在QueryOptimizer
里的,orderPlansBestToWorst
返回一个排好序的plan列表,取第一个就是最终的plan。
优化方法
从上面的流程中可以看出,JoinCompiler.optimize
,compileJoinQuery()
,compileSingleFlatQuery()
还有后面的getApplicablePlans()
和orderPlansBestToWorst()
这些地方可以进行优化。