我們看看 TiDB 一段代碼的實現 --- 左外連接(Left Out Join)的消除;
select 的優化一般是這樣的過程:
在邏輯執行計划的優化階段, 會有很多關系代數的規則, 需要將邏輯執行計划(LogicalPlan)樹應用到各個規則中, 嘗試進行優化改寫;
我們看看其中的一條優化規則: outerJoinEliminator
TiDB作為優秀的開源項目, 代碼的注釋也非常優秀, 里面提到了滿足這些條件的 Left Outer Join 可以消除右表;
// tryToEliminateOuterJoin will eliminate outer join plan base on the following rules
// 1. outer join elimination: For example left outer join, if the parent only use the
// columns from left table and the join key of right table(the inner table) is a unique
// key of the right table. the left outer join can be eliminated.
// 2. outer join elimination with duplicate agnostic aggregate functions: For example left outer join.
// If the parent only use the columns from left table with 'distinct' label. The left outer join can
// be eliminated.
我們這里只討論第一種情況, 第二種情況請您自行查看源碼;
我們構造滿足第一種情況的查詢:
左表:
t1(
id int primary key not null auto_increment,
a int,
b int
);
右表:
t2(
id int primary key not null auto_increment,
a int,
b int
);
查詢語句:
select t1.id, t1.a from t1 left join t2 on t1.id = t2.id;
我們看看優化規則之前的邏輯執行計划:
這個執行計划是這樣的:
頂層的算子是投影(Projection)操作, 取 t1.id 和 t1.a 這兩列;
接下來是 連接(Join) 操作, 類型是: LeftOuterJoin;
接下來左邊是 OuterPlan, 左表; 右邊是 InnerPlan, 右表;
左邊的算子是掃 t1 的數據, 右邊的算子是掃 t2 表的數據;
底層的算子將數據返回給上層的算子, 來完成計划的執行;
注, 這種數據自底向上的流動方式有點像火山噴發, 所以這種執行模型叫做火山模型(Volcano);
主要代碼邏輯在這里:
outerJoinEliminator::doOptimize
這是一個遞歸的操作, 不斷的獲取 parentCols, 並對 LeftOuterJoin 或者 RightOuterJoin 嘗試進行消除;
如果是LeftOuterJoin , 嘗試消除右表, 如果是RightOuterJoin, 嘗試消除左表;
因為我們這里只有 Projection算子 和 LeftOuterJoin算子, 所以代碼調用邏輯基本是這樣的:
* 獲取Projection的列
* 對下面的LeftOuterJoin進行判斷
* 獲取左表的列: outerPlan.Schema().Columns
* 判斷上層 Projection 用到的列是否全部來自左表: o.isColsAllFromOuterTable(parentCols, outerUniqueIDs)
* 獲取 Join 連接的列: innerJoinKeys := o.extractInnerJoinKeys(p, innerChildIdx); 這即是右表的 t2.id
* 判斷連接的列是否被包含在右表的主鍵: o.isInnerJoinKeysContainUniqueKey(innerPlan, innerJoinKeys)
* 滿足條件, 將 LeftOutJoin 替換掉;
我們展示一下這個轉換:
上圖中灰色的執行計划會被消除掉;
變成了下面的執行計划:
最終, 上面給出的sql 的例子等價於下面的語句:
select t1.id, t1.a from t1;
有興趣的讀者可以看看其他的滿足條件的左外連接消除的邏輯, 這里就不講了;
邏輯優化的過程一般被叫做RBO(rule based optimization);
邏輯規則的優化是基於關系代數的等價推導和證明;
大部分數據庫(例如mysql, Oracle, SQLServer)的邏輯優化規則都類似, 可以互相參考;
物理優化的過程一般被叫做CBO(cost based optimization);
不同的數據庫的物理優化規則不一定是一樣的, 這個可能根據數據和索引的存放特點來進行針對性的處理;