最佳化器-RBO 的規則轉化

KaiwuDB發表於2023-03-14

一、 RBO 背景介紹

RBO( Rule-Base d Optimization, 基於規則的最佳化器)有著一套嚴格的使用規則,按照 RBO 去寫 SQL 語句,無論資料表中的內容怎樣,也不會影響到你的“執行計劃”。


換言之 RBO 對資料不“敏感”,它根據指定的優先順序規則,對指定的表進行執行計劃的選擇。比如在規則中,索引的優先順序大於全表掃描。RBO 是根據可用的訪問路徑以及訪問路徑等級來選擇執行計劃,在 RBO 中,SQL 的寫法往往會影響執行計劃。


二、 Optgen 介紹

Optgen 是一種域細節語言 ( DS L),它提供了一種直觀的語法來定義、匹配、替換目標表示式樹中的節點,最佳化器規則的編寫便是基於這種語言。


程式碼中存在這樣的模組:將 DSL 語言轉化為真實的 go 語言( 檔案字尾 og.go),以便最佳化器呼叫。模組入口在 pkg/sql/opt/optgen/cmd/optgen/main.go 中的 func main(),這裡暫不涉及,以下介紹中此模組簡稱“ 程式碼生成模組”。


三、RBO 規則介紹

RBO 涉及的規則定義在 kaiwu/pkg/sql/opt/norm/rules/*.opt 中。


1.關係代數的 9 種操作

關係代數中包括了:並、交、差、乘、選擇、投影、聯接、除、自然聯接等操作。其中五個基本操作為並( )、差( -)、笛卡爾積( ×)、投影( π)、選擇( σ)。


2.關係代數表示式

由關係代數運算經有限次複合而成的式子稱為關係代數表示式,這種表示式的運算結果仍然是一個關係,可以用關係代數表示式表示對資料庫的查詢和更新操作。


3.關係代數表示式的轉換

若兩個關係表示式在每一個有效資料例項中都會產生相同的結果集,則可以稱他們是等價的(元組的順利是無關緊要的,而且不能說明任何表示式更優於其他表示式)。


合取選擇運算可以分解為單個選擇運算,稱為選擇運算的級聯:

 


選擇運算具有交換律:



投影在合理的情況下,只有最後一個有效:



選擇操作可與笛卡爾積以及連線相結合:



連線操作滿足交換律:



自然連線滿足結合律:



4.RBO 轉化例項

語句如下:

  • select course_id, title from course;

  • select * from teaches join ① on teaches.course_id = ① .course_id;

  • select * from instructor join ② on Instructor.ID = ② .ID;

  • select name, title from ③ where dept_name = Music and year = 2009;


執行 ④ 語句, 轉換前的表示式樹和轉換後的表示式樹如下:


在這次轉換工程中,使用了謂詞下推,結合律,轉換後的表示式樹一定優於前面的表示式樹,這就稱為 RBO,基於規則的轉換。


5.RBO 基本規則

(1)列裁剪

Select a from t where b >5;

我們可以將 t 表中的所有資料讀取上來,然後根據條件過濾,然後再投影,最後拿到列(a)的資料。也可以先進行列剪裁,先把 a,b 資料讀取,然後根據過濾條件進行過濾,最後輸出資料。


(2)最大最小消除

Select min( ID) from t;

這句話可以轉換成 Select id from t order by id desc limit 1;


(3)投影消除

如果一個投影的輸入和輸出列是一樣的,那麼這個投影是無用的。


(4)謂詞下推

儘量把選擇的運算元推到葉子節點,這樣可以大大減少上面每個表示式節點的消耗。


考慮這樣一個句子: Select * from t1,t2 where t1.a > 4 and t2.b >5;


如果先進行笛卡爾積在進行過濾條件時,則會產生很多不必要的元組,但是如果先過濾t1,t2 的關係,在進行笛卡爾積,那麼表示式的消耗將大大減少。


在進行過濾時,儘可能準確到一個 select 運算元,如若不能,則在具有過濾需要的列及時處理,比如 a.a > 5 and b.b > 10 and a.c > a.b 第一個和第二個條件都可以推到 select 運算元中,在這個運算元上面立即加一個 a.c > a.b 的過濾條件。


四、規則生成原始碼介紹

1.生成程式碼模組定位,引數解釋

入口函式 pkg/sql/opt/optgen/cmd/optgen/main.go 中的 func main(),如圖所示:



Kaiwu/Makefile 中呼叫這個函式,需要輸入 5 個引數 os.Args,這些引數依次如下(以探索階段涉及的 factory.og.go 為例):


  • -out :輸出檔案標籤

  • 輸出檔名 :(pkg/sql/opt/xform/factory.og.go)

  • 命令標籤 :(compile/explorer/exprs/factory/ops/ rulenames)

  • 結構定義檔案 :(pkg/sql/opt/ops/*.opt)

  • 規則原始檔 :(pkg/sql/opt/norm/rules/  opt/pkg/sql/opt/xform/rules/.opt)


Makefile 程式碼定位如下:



2.除錯建議

調式某個 opt 檔案生成 factory.og.go 檔案,如 norm/rules/comp.opt,可以採用如下方式:



3.重要階段介紹

(1)流程圖中重要函式

a.   以 pkg/sql/opt/norm/rules/comp.opt 檔案為例進行流程分析:

run(),Parse(),parseOne()函式




進入 Parse()函式——>parseOne():


① Parse()函式對parsed, args賦值;

② parseOne() 函式:裁剪 args a 和 b 引數,剩下 c、d、e;對 FlagSet 結構體中的 actual 賦值;

③ 將生成檔案 b 引數賦值給 Flag 中的 Value。

 


b.  g.globResolver(source)函式是將 pkg/sql/opt/ops/*.opt 中的檔案 append 到 files 檔案中,並將規則檔案 pkg/sql/opt/norm/rules/comp.opt 檔案加入到 file 中。


c.   NewCompiler(files...)函式: 構建 Compiler 結構體,將 files 檔案匯入:


d.   compiler.Compile()→ Parse()→ parseRoot()→ p.scan():


p.scan(): 根據返回值執行不同的操作


① WHITESPACE: 清除空白

② COMMENT: 向 Parser 中的 comment 中新增註釋

③ LBRACKET:返回上層函式 parseRoot()

④ parseTags(): 解析出規則名和標準

⑤  parseRule():解析出具體規則


e.   compiler.Compile()中的 compileDefines()函式:


將 defines 中的內容賦值給 Compiler→compiled→defineIndex中。

將 define 中的 Tags 賦值給 unique 中。


f.   compiler.Compile()→compileRules()→ruleCompiler.compile():


將 pkg/sql/opt/ops/*.opt 檔案中定義的後設資料和 pkg/sql/opt/norm/rules/camp.opt 中所定義的規則相結合生成新規則,存於  ruleCompiler→compiled→Rules 中。


(2)程式碼流程圖如下




五、總結

以上就是 RBO 的規則轉化在資料庫中的功能方式,透過規則查詢最佳化器執行一個預設的計劃,在此預設規則下,大幅提升執行效率。


來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/70027415/viewspace-2939572/,如需轉載,請註明出處,否則將追究法律責任。

相關文章