CBO與RBO並非對立關係,而是基於RBO的擴充
CBO = RBO + Cost Model + Model Iteration,透過代價模型,在一定的時間空間範圍內透過動態規劃演算法來獲得最終的執行計劃
claicte的最佳化原理是,它假定如果一個表示式最優,那它的區域性也是最優的。成本最優假設利用了貪心演算法的思想,在計算的過程中, 如果一個方案是由幾個區域性區域組合而成,那麼在計算總成本時, 我們只考慮每個區域性目前已知的最優方案和成本即可。
Cost(A)∼Cost(B)+Cost(C)
Calicte提供的最佳化器:
HepPlanner(RBO)
簡單理解就是兩個迴圈,第一個迴圈會遍歷節點,第二個迴圈規則進行匹配,開始和結束會有RelNode與HepRelVertex的轉換
- setRoot 方法將每一個RelNode轉換為相應的HepRelVertex,作為頂點最終構建出一個有向無環圖(DAG)
- 呼叫findBestExp方法匹配規則、應用規則
- executeProgram() 會遍歷所有註冊的規則,然後進行匹配。規則的制定是透過 withOperandSupplier 來實現的,需要傳遞一個只有 apply() 的 OperandTransform 函式式Function,入參 OperandBuilder,出參 Done,代表已經完成。OperandBuilder 是制定規則的關鍵,透過呼叫 operand(Class)傳入相應節點的 RelNode 的 Class 便可以定義該規則
- Project 節點下面沒有任何子節點輸入withOperandSupplier(b0 ->b0.operand(Project.class).noInputs)
- Project節點的一個輸入是Join運算元withOperandSupplier(b0 ->b0.operand(Project.class).oneInput(b1 ->b1.operand(Join.class).anyInputs()))
- FilterJoinRule:withOperandSupplier(b0 -> b0.operand(Filter.class).oneInput(b1 -> b1.operand(Join.class).anyInputs()))
- 遍歷的每一條規則透過 applyRule() 方法判斷該規則是否匹配,如果匹配則返回轉換後的節點,如果不匹配則繼續迴圈,當匹配次數達到最大值時就會結束迴圈,得到最佳化後的DAG
- ARBITRARY:任意匹配方式,該方式和深度優先遍歷是一樣的,採用的也是深度優先的方式。
- BOTTOM_UP:從葉子節點開始匹配一直到根節點,一種從下到上的方式。
- TOP_DOWN:從根節點開始匹配,一直到葉子節點,一種從上到下的方式。
- DEPTH_FIRS:深度優先遍歷。
- buildFinalPlane() 將每一個節點轉化為RelNode並返回
VolcanoPlanner(CBO)
不是簡單地應用rule,而是會使用貪心、動態規劃演算法,計算每種rule匹配後生成新的SQL樹的cost,與原先SQL樹的Cost資訊相比較,如果新的樹的Cost比較低,那麼才會真正應用對應的rule
Calcite當中預設提供了資料行數、CPU代價、I/O代價,透過這3個方面來影響一個規則的好壞
貪心:認為多個區域性的最優代價相加後即是全域性的最優代價
動態規劃:分解子問題,複用已經求解的子問題
- setRoot()呼叫registerImpl(),遍歷RelNode樹的所有節點,addRelToSet()計算並記錄每個節點的代價,如果有等價表示式同時它的代價更小,更新這個RelSubset
- 先遍歷節點再遍歷規則,若匹配則會呼叫 fireRules() 將規則加到 RuleQueue 佇列
- findBestExp() 透過 RuleDriver 死迴圈地從 RuleQueue 中取出規則,兩個條件可以打破迴圈
- 規則佇列中彈出的規則為null
- 丟擲VolcanoTimeoutException異常
- RelSubset#buildCheapestPlan 遞迴地組裝每一個節點的最優解,返回最佳化後的RelNode樹