1. 程式人生 > 資料庫 >做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(一)

做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的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的【請看下一篇:做一個平臺,讓對手發來一個很爛的sql,然後系統返回一個優化好的sql(二)】