做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(一)
阿新 • • 發佈:2020-11-16
我們要做的事情大概分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的【請看下一篇:做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(二)】