基于calcite做傻瓜式的sql优化(一)
我们要做的事情大概分3步:
1、需求的描述(主要把问题点抛出来)
2、解决如何优化sql性能的问题(hive关于一条sql的生命周期源码的分析),也就是如何对你的sql进行RBO和CBO的优化
3、解决如何自动识别sql元数据的问题(antlr关于如何深度优先遍历语法树)
背景:
我们有一套智能中台系统,可以基于配置化的方式就可以做出各种图表。说白了就是可以快速给业务通过图表的方式展现数据
需求:
通过配置来快速展现数据固然是好的事情,但稍微思考一下其实也是通过配置的方式来生成sql; 那么生成的这个sql性能不见得会很好吧?(其实会非常的不好)
比如,我们通过配置的方式生成的sql大概这个样子:
select t0.tree_id, sum(t0.gap) as num from ( SELECT w.tree_id, w.gap, r.executed_sql FROM data_middleground.view_mkt_node_kpi_warning w JOIN data_middleground.view_mkt_node_result r ON w.tree_id = r.tree_id WHERE w.warning_status = 0 and r.is_del = 0 ) t0 where t0.gap > 1 group by t0.tree_id order by tree_id desc
花一分钟仔细看下这个sql,会发现其实很烂,性能点在:
1、表关联的时候,谓词没有下推 2、多个谓词分别出现在关联和聚合的地方,没有做过滤合并和下推 3、有聚合操作,是否可以聚合下推?
烂sql的执行计划:
LogicalSort(sort0=[$0], dir0=[DESC]) LogicalAggregate(group=[{0}], num=[SUM($1)]) LogicalProject(tree_id=[$0], gap=[$1]) LogicalFilter(condition=[>($1, 1)]) LogicalProject(tree_id=[$0], gap=[$1], executed_sql=[$4]) LogicalFilter(condition=[AND(=($2, 0), =($5, 0))]) LogicalJoin(condition=[=($0, $3)], joinType=[inner]) EnumerableTableScan(table=[[data_middleground, view_mkt_node_kpi_warning]]) EnumerableTableScan(table=[[data_middleground, view_mkt_node_result]])
那么能否通过一套代码处理后,让“烂sql1”进入代码,出来后是“好sql2”
先看下成果,优化后的sql:
SELECT `t0`.`tree_id`, SUM(`t0`.`gap`) AS `num` FROM ( SELECT * FROM `data_middleground`.`view_mkt_node_result` WHERE `is_del` = 0 ) AS `t` INNER JOIN ( SELECT * FROM `data_middleground`.`view_mkt_node_kpi_warning` WHERE `warning_status` = 0 AND `gap` > 1 ) AS `t0` ON `t`.`tree_id` = `t0`.`tree_id` GROUP BY `t0`.`tree_id` ORDER BY `t0`.`tree_id` IS NULL DESC, `t0`.`tree_id` DESC
请花一分钟看下sql,会发现这真TM是个好的sql;
执行计划:
LogicalSort(sort0=[$0], dir0=[DESC]) LogicalAggregate(group=[{0}], num=[SUM($1)]) LogicalProject(tree_id=[$0], gap=[$1]) LogicalProject(tree_id=[$0], gap=[$1], executed_sql=[$4]) LogicalJoin(condition=[=($0, $3)], joinType=[inner]) LogicalFilter(condition=[AND(=($2, 0), >($1, 1))]) EnumerableTableScan(table=[[data_middleground, view_mkt_node_kpi_warning]]) LogicalFilter(condition=[=($2, 0)]) EnumerableTableScan(table=[[data_middleground, view_mkt_node_result]])
那么如何实现上面的功能呢?可以参考spark、hive、druid等,他们是如何做的sql优化。这里我参考的是hive;
那么接下来通过走读hive源码的方式来看下,hive是如何处理一条sql的【请看下一篇:基于calcite做傻瓜式的sql优化(二)】