優化器(The Optimizer)
這篇描述MySQL查詢優化器的工作原理。 MySQL查詢優化器主要為執行的查詢決斷最有效的路線(routine,走向)。
一。原始碼與概念
這部分討論優化器關鍵概念,術語,及在MySQL原始碼怎麼對應的。
1.定義
狹義定義:最佳化器,就是DBMS為查詢時決斷要往哪種執行路徑的一系列路線。
MySQL是經常調整查詢的路線,所以你得把這篇描述的邏輯和在原始碼裡的做比較。為了讓比較容易些,這裡會包含相關檔案和路徑的註解,例如原始碼/sql/sql_select.cc,optimize_cond()函數。
當一個查詢被轉換成另一個查詢時,其結果是一樣的,就會發生語句轉換。以下這個查詢
SELECT ... WHERE 5 = a
就會被轉換變成
SELECT ... WHERE a = 5
最明顯的語句轉換是少的,有些語句轉換,是為了更快的執行。
2.最佳化器原始碼
如下偽代碼顯示了/sql/sql_select.cc中handle_select()函數的邏輯結構。 (原始碼/sql/sql_select.cc處理SQL查詢)
handle_select()
mysql_select()
JOIN::PRep /* optimizer is from here ... * /
optimize_cond()
opt_sum_query()
choose_plan()
/* Find the best way to access tables */
optimize_straight_join()
best_access_path ()
/* Find a (sub-)optimal plan among all or subset */
/* controls the exhaustiveness of the search. */
greedy_search()
()
best_access_path()
make_join_select() /* ... to here */
JOIN::exec()
縮顯示了哪個函數呼叫哪個函數,如handle_select()函數呼叫mysql_select()函數,mysql_select()函數會呼叫JOIN::prepare()、JOIN::optimize()、JOIN::exec(),以及類別推。 mysql_select()函數的第一部分是呼叫JOIN::prepare(),此函數用來上下文分析、元資料建立和一些語句轉換。查詢最佳化器函數JOIN::optimize()和其所有最佳化處理中的子路線。當執行完JOIN::optimize()函數後,JOIN::exec()接手並完成JOIN::optimize()函數最佳化決斷後的執行工作。
雖然有JOIN字出現,其實查詢優化器的工作會處理所有的查詢類型,不單單JOIN聯接查詢。
二。首要最佳化
這部分討論伺服器執行的最重要最佳化。
1.最佳化常數關係
常數等值傳遞
lumWHERE column1 = column2 AND cocolum =B && B=C => A=C(可等值傳遞),上句表達式轉換後變成:
WHERE column1='x' AND column2='x'
=, , =, , , LIKE
原始碼見/sql/sql_select.cc,change_cond_ref_to_const()函數。或/sql/sql_select.cc,propagate_cond_constants()函數。
剔除死代碼
總是TRUE的條件會發生語句轉化,如:
WHERE 0=0 AND column1='y'
這種情況下,第一個條件會被剔除,最後一個條件會被剔除,最後一個條件:
column1='y'
原始碼見/sql/sql_select.cc,remove_eq_conds()。
總是FLASE的條件也會發生語句轉化,如:
WHERE (0 = AND s1 = OR s1 = 7
小括號和前兩個條件總是FLASE,最後為:
= 7
還有一些情況下,當WHERE語句中代表不可能的條件,查詢最佳化器可能會全部剔除語句,如下:
WHERE (0 = AND s1 = 5)
因為這條件永遠不會為TRUE,在EXPLAIN分析中會顯示Impossible WHERE。這樣
最後為:
WHERE column1 = 3
在先前說的常數等值傳遞,最佳化器很容易將此查詢語句合併在一起。和常數表
MySQL常數值,有時不單單指在查詢的SQL語句的字面意思上,也可在常數表(constant tables)的內容裡。無記錄或一筆記錄的表
2。鍵的欄位定義為NOT NULL)
例如,Table0表的定義包含:
... PRIMARY KEY (column1,column2)
然後,查詢表達式:
F AND column2=7 ...
會傳回常數表(constant tables)。更簡單地,如果Table1表的定義包含:
... unique_not_null_column INT NOT NULL UNIQUE
然後,查詢表達式:
FROM Table1 ... WHERE unique_not_null_column tables)。
SELECT Table1.unique_not_null_column, Table2.any_column
FROM Table1, Table2
MySQL評估這語句時,首先會發現,依照常數表(constant tables)的第二點定義,查詢條件為unique_not_null_column的表Table1是一個常數表(constant tables),它就會得到這個值。
如果取值失敗,也就是在表Table1裡unique_not_null_column = 沒值,EXPLAIN後結果:Impossible WHERE noticed after reading const tablesd
Impossible WHERE noticed after reading const tablesdnot,如果取值成功,也就是在表值一筆記錄值,MySQL會轉換為下列語句:
SELECT 5, Table2.any_column
FROM Table1, Table2
事實上,這是一個很好的例子。優化器因前面提到的常數等值傳遞進行一些語句轉換。另外,為什麼要先描述常數等值傳遞,因為它在MySQL確認什麼是常數表(constant tables)前就先進行了。優化器步驟的順序,有時是有差異。
雖然很多查詢都沒常數表(constant tables)參考。應該牢記,以後無論什麼時候,常數constant字被提及,它是指任何一個字面上的值或一個常數表(constant tables)的內容。
2.最佳化JOIN連結
這部分討論最佳化JOIN連結的不同方法。注意:JOIN聯結並非單單指JOIN類型,而是所有條件查詢的類型。有些人喜歡叫access type。
確定JOIN聯結類型
當評估查詢條件表達式時,MySQL會確定它是屬於哪個JOIN聯接類型。
如下有記錄在檔的JOIN類型,從最好到最壞的排序下來:
system:常數表(constant tables)的system類型
const:常數表(constant tables)
eq_ref:相等關係的唯一或主鍵索引
ref:相等關係的索引,此索引的值不能為NULL
ref_or_null:相等關係的索引,此索引的值可能為NULL
range:有關聯的索引,如BETWEEN,IN,>=,LIKE等
index:順序掃描索引
ALL:順序掃描整個表格資料
原始程式碼請參閱/sql/sql_select.h,enum join_type{}。另外,還有一小部分沒記錄在檔,為了子查詢的JOIN聯結類型。
優化器利用JOIN聯接類型選擇一個驅動表達式,如下:
SELECT *
FROM Table1
WHERE indexed_column = AND unindexed_column = 60cDlumn = 6969式。對它來說,你也會遇到各種不同的例外,但對這句話描述,是第一個簡單的最佳化法則。
最壞執行計劃:掃描讀表的所有行。這也叫Table1的順序掃描或簡單表格掃描。查詢每行,檢查indexed_column和unindexed_column兩列的值是否符合查詢的條件。
最好執行計劃: 透過索引,檢索哪些有indexed_column = 值的記錄。這也叫被索引的搜尋。查詢每行,檢查unindexed_column列的值是否符合查詢的條件。
完成搜尋一條最佳QEP的關鍵路線請見sql/sql_select.cc,find_best()。它執行了所有可能計劃的詳盡搜索,從而保證它最終將找到一個最佳的一條。
procedure find_best(
partial_plan in, /* in, 目前連接的表的部分計劃*/
partial_plan_cost, 中未引用的表集*/
best_plan_so_far, /* 輸入/輸出,迄今為止找到的最佳方案*/
best_plan_so_far_cost)/* 輸入/輸出,best_plan_so / * 計算
最佳化器考慮的因素可能包括:
中有許多行(不好)
限制(好)
長鍵(好)
唯一或主鍵(良好)
全文鍵(壞)
平均/最大鍵長短
小表檔案
關卡很少在索引中
所有ORDER BY / GROUP 欄位都來自此表*/
cost = complex-series-of-calculations;
/* 將成本加到目前的成本中。 */
partial_plan_cost+= cost;
if (partial_plan_cost >= best_plan_so_far_cost)
partial_plan=透過best_access_method擴充partial_plan;
剩餘表=剩餘表-表T;
if (remaining_tables 不是空集)
{
best_plan_so_far, best_plan_so_far_cost);
best_plan_so_far=partial_plan;
}
}
這裡優化器使用了一種深度優先搜尋演算法。它完成在 FROM 語句中評估所有的表。如果評估金斯目前狀況最好的評估,變得更差,將停止搜索。掃描的順序依賴於出現FROM語句中的表格的順序。
原始碼見:/sql/table.h, struct st_table。
分析表(ANALYZE TABLE)可能會影響一些最佳化器決斷的參數。原始碼請參閱:/sql/sql_sqlect.cc,make_join_statistics()。
find_best()和greedy_search()的直截了當(straightforward)使用將用於不會LEFT JOIN或RIGHT JOIN。例如,從MySQL 4.0.14開始,在某些這種情況下,優化器可能會將 LEFT JOIN 轉變為 STRAIHT JOIN,並交換錶的順序。另外請參閱:LEFT JOIN 和 RIGHT JOIN 優化。
RANGE 連接類型
有些條件使用索引,但是在一個可以鍵的範圍(range)或寬度內。這些稱為範圍條件,最常的是帶>,>=,
column1 IN (1,2,3)
如下語句,最佳化器也需要使用索引(範圍查詢範圍搜尋)
但不行:
column1 KE的第一個字元一個是通配符那就,沒範圍查詢。
同樣,如下兩個語句也是一樣的
column1 BETWEEN 5 AND 7
和
如果查詢的條件,檢索了太多的索引鍵,優化器可能轉變RANGE連接類型為ALL JOIN連接類型。像這種轉變,特別可能在條件和多層第二索引(secondary indexes)中。原始碼請參閱:/myisam/mi_range.c,mi_records_in_range()(MyISAM索引)。
收到像這種方式的索引,一般稱為覆蓋索引(COVERING INDEX)。在EXPLAIN Extra描述中,MySQL會簡單地用INDEX單字來表示覆蓋索引(COVERING INDEX)。
INDEX MERGE聯接類型
概述
使用索引合併(INDEX MERGE),當表的條件可轉換成如下:
SELECT * FROM t WHERE key1=c1 OR key2
單一SEL_TREE物件無法建構成在OR語句中有不同成員的鍵的條件,類似這條件:
key1
sel_imerge_cond = (t_1 OR t_1 OR ... OR t_n)每個t_i(t_1,t_2。)代表一個SEL_TREE,沒有一對。 (t_i,t_j)不同的SEL_TREE物件能被合併成單一的SEL_TREE物件。
目前的實現方法在建構SEL_IMERGE時,只有當沒有單一的SEL_TREE物件能被建構成被分析過的查詢的一部分;如果發現單一SEL_TREE物件能被建構,就會馬上丟棄SEL_TREE。這實際上是一個限制,並且可能導致最壞行檢索策略的使用。下列查詢: 在badkey的掃描會被選擇,即使在(goodkey1,goodkey1)上的索引合併(IND在badkey的掃描會被選擇,即使在(goodkey1,goodkey1)上的索引合併(INDEXEXEXEX)快。
(t_21 OR t_22 OR ... OR t_2l) AND (t_21 OR t_22 OR ... OR t_2l) AND ... AND
(t_M1 OR t_M2 OR . .. OR t_mp)
最小成本的SEL_IMERGE物件用來檢索行。
索引合併(INDEX MERGE)建構子的詳細資訊請參閱:原始碼sql/opt_range.cc,imerge_list_and_list(),imerge_list_or_list(),和SEL_IMERGE類別的成員函數。
為了範圍RANGE查詢,MySQL最佳化器建立一個SEL_TREE對象,如下這種形式:
range_cond = (cond_key_1 AND cond_key_2 都是 cond_key_2的組成部分的條件。 MySQL為每個有用的鍵建立一個cond_key_i條件。然後這種成本最便宜的條件cond_key_i用來做範圍RANGE掃描。
單一的cond_key_i條件是用SEL_ARG物件中的一個相聯指標網(a pointer-linked network of SEL_ARG objects)來表示。每個SEL_ARG對象參考鍵的特定部分和表示如下的條件:
sel_arg_cond= (inf_val AND next_key_part_sel_arg_cond (2)
OR left_sel_arg_cond (3)
OR right_sel_arg_cond (4)
1。實現間隔,可能沒有上下臨界,或包括或不包括臨界值。
2。實作SEL_ARG物件以下一個鍵組件作為條件(is for a SEL_ARG object with condition on next key component)。
3。實作有間隔的SEL_ARG對象,在同樣區域作為這個SEL_ARG物件(is for a SEL_ARG object with an interval on the same field as this SEL_ARG object)。在目前物件和左邊物件中的間隔,是不相交的。 left_sel_arg_cond.sup_val
4。實現有間隔的SEL_ARG對象,在同樣區域作為這個SEL_ARG對象。在目前物件和右邊物件中的間隔,是不相交的。 left_sel_arg_cond.min_val >= max_val。
MySQL會轉換任意深度的嵌套AND-OR條件為上述相連接的形式。
行檢索演算法
索引合併(INDEX MERGE)有以下兩個步驟:
準備階段:
activate 'index only';g next (key, rowid) pair from key_i)
{
if (no clustered PK scan ||
row doesn't match clustered scan row doesn't match clustered PK scan con
deactivate 'index only';
行檢索階段:
for each rowid in Unique
retrieve row and pass it to output;
}if (clustered_pk_scan){fluf {p output;
}原始程式碼請參閱:sql/opt_range.cc,QUICK_INDEX_MERGE_SELECT類別函數的索引合併(INDEX MERGE)行檢索程式碼。
3.換位(Transpositions)
MySQL支援簡單語句表達式的換位(反轉關係運算子的運算元的順序)。換句話說:
WHERE - 5 = column1
而這句話不能同等對待:
WHERE column1 = -5
AND關係
一個AND的查詢形式如condition1 AND condition2,如下:
WHERE column1 = 'x' AND column2 = 'y'
WHERE column1 = 'x' AND column2 = 'y'WHERE column1 = 'x' AND column2 = 'y'
375:解說如果兩個條件都沒被索引,使用順序掃描(全表掃描)。2。除前一點之外,如果其中一個條件有更好的JOIN聯接類型,則以JOIN聯接類型選擇一個驅動器。 (請參閱確定JOIN聯接類型)
3。除前兩點之外,如果兩個條件都有加索引且平等的JOIN聯接類型(註:JON 聯接類型效果有好壞之分),則以第一個建立的索引選擇一個驅動。 最佳化器也會根據索引交叉選擇索引合併(INDEX MERGE),請參閱 INDEX MERGE連接類型。 例子如下:CREATE TABLE Table1 (s1 INT, s2 INT);
CREATE INDEX Index1 ON Table1 (s2);
);
CREATE INDEX Index2 ON Table1 (s1);
. s2 = 5;
OR關係
一個OR的查詢形式如condition1 OR condition2,如下:
WHERE column1 = 'x' OR column2 = 'y'
這種查詢最佳化器的決斷是使用順序全表掃描。
還有一個選擇在特定的環境下會使用索引合併(INDEX MERGE),更多資訊請參閱INDEX MERGE優化器和Index Merge Optimization。
上述的特定情況不能用於如果兩個條件的欄位是一樣。如下:
WHERE column1 = 'x' OR column1 = 'y'
這種情況,由於語句是RANG查詢,所以會走索引的。這個話題會在IN限定(predicate)的討論中再次看到。
UNION查詢
所有含有UNION的SELECT查詢語句會被各自最佳化。因此,這個查詢:
SELECT * FROM Table1 WHERE column1 = 'x'
UNION ALL
SELECT * FROM TABLE1 WHERE column2 = 'y'
如果column1和column2ECT的結果集會被合併。注意:此查詢可能產生相同的結果集,如果查詢使用了順序掃描OR的範例。
NOT()關係
一個邏輯條件如下:
column1 5
等價於:
column1 5
等價於:
WHERE NOT (column1 != 5)
WHERE column1 = 5
WHERE column1 = 5
我們期待能針對上述兩種情況加入新的最佳化方法。
ORDER BY語句
通常,如果最佳化器發現行記錄不管怎麼樣都是有順序的,在ORDER BY語句中它也會跳過SORT過程。但是還是驗證幾個例外的情況。
例:
優化器會丟掉ORDER BY語句,這也是死程式碼刪除範例。
優化器會使用column1的索引,如果有的話。
例:
最佳化器會使用column1的索引,如果有的話。但是不要被弄混了,索引只用來找出記錄值。另外:順序掃描索引的成本比順序掃描全表的成本是更便宜的(一般索引的大小會比資料值的大小小的),這也是為什麼INDEX JOIN聯結類型會比ALL型別更優化。請參閱確定JOIN聯接類型。
還有一個結果集的全部排序SORT,例如:
SELECT * FROM Table1
WHERE column1 > 'x' AND column2 > 'x'的,優化器會走在column1上的索引。在這個查詢語句,對column2值的排序不會影響驅動的選擇。
原始碼請參閱:/sql/sql_select.cc,test_if_order_by_key()和/sql/sql_select.cc,test_if_skip_sort_order()。
GROUP BY和相關的條件
這裡描述了GROUP BY和相關條件(HAVING,COUNT(),MAX(),MIN(),SUM(),AVG(),DISTINCT())的主要主要的主要最佳化.
GROUP BY會使用索,如果一個索引存在的話。
GROUP BY會用排序,如果沒有索引存在。優化器可能選擇使用HASH表排序。
GROUP BY x ORDER BY x的情況,最佳化器會因為GROUP BY會以 x 的排序,而認為ORDER BY是不需要的。
最佳化器包含了為轉移特定HAVING條件的WHERE語句中的程式碼。然而,此程式碼在編寫時是不生效的。原始碼請參閱:/sql/sql_select.cc,JOIN::optimize(),在#ifdef HAVE_REF_TO_FIELDS之後。
SELECT COUNT(*) FROM Table1;
不必掃描所有行,就能得到行總數值。這只對MyISAM表是正確的,但不適合InnoDB表。另外這個查詢
SELECT COUNT(column1) FROM Table1;
不會有同樣的最佳化,除非column1定義為NOT NULL。
MAX()和MIN()新的最佳化方法。例:
SELECT MAX(column1)
WHERE column1 如果column1被索引了,就很容易找到最大值通過查詢索引中的'a'值並且在這之前返回索引鍵。
SELECT column1 FROM Table1 GROUP BY column1;
當且僅當這兩個條件都是正確:
* GROUP BY能透過索引來未完成。這暗示了只有一個表在FROM語句中且沒有WHERE語句。
* 沒有LIMIT語句。
因為DISTINCT語句並不總是轉換成GROUP BY,不要期望含有DISTINCT查詢語句總是會有被排序的結果集。然而,你能依賴GROUP BY優化規則,除非查詢包括ORDER BY NULL。
三。其它最佳化
這部分,討論其它更特別的最佳化方法。
1. ref和eq_ref的NULLs值過濾存取
這部分討論ref和eq_ref聯接類型的NULLs值過濾最佳化方法。
前期(early)NULLs值過濾
假設我們有個聯接順序如下:
..., tblX, ..., tblY, ...
更深入假設,表blref類型被存取:
tblY.key_column = tblX.column
或者,使用多個鍵部分的ref類型存取:
... AND tblY.key_partN = tblX.column AND ...
...AND tblY.key_partN = tblX.column AND ...
(tblY.key_partN = tblX.column) => (tblX.column IS NOT NULL)
ref分析器(包含方法update_ref_and_keys())透過設定KEY_FIELD::null_rejecting=TRUE檢查和標記像上述類型的等式查詢。
tblX.key_part1 = expr1 AND tblX.檢索前,我們確定任何expri(expr1,expr2,expr3。。)值是否為NULL。如果是,我們不會呼叫檢索,而是會馬上返回沒找到符合數組。
這個最佳化方法重複使用了由前期(early)NULLs濾波產生的null_rejecting屬性。這個檢查的源代碼見:函數join_read_always_key()。
2.分區相關的最佳化
這部分討論MySQL分區相關的最佳化。 MySQL5.1分區相關概念與實作請參考:Partitioning。
分區裁切(pruning)
分區裁切(partition pruning)的操作,如下定義:
「提供一個分區表的查詢,比對此分區表的DDL3查詢和任何查詢中的任何語句,並且找出這查詢存取的最小分區集。沒被加入這個分區集的其它分區,就不會被訪問的,也就是說被裁剪掉的分區。正因為這樣,查詢的執行速度變得更快。
Non-Transactional Table Engines.??如MyISAM無事務儲存引擎,鎖會被加在整個分區表。理論上講,使用分區裁剪(partition pruning)是有可能提高並發,只把鎖加在被使用的分區上。但是目前還沒實現這功能。
分區裁剪(partition pruning)不依賴表的儲存引擎,所以這功能是MySQL查詢優化器的一部分。接下來章節描述分區裁切(partition pruning)的細節。
區間圖interval graph是由下而上的方式建構成,並來表示上述步驟的描述。接著討論,我們會先定義術語區間圖interval graph,接著描述怎樣用分區區間來組成一個區間圖interval graph,最後描述區間圖interval graph的工作流程。
分區區間(Partitioning Intervals)
CREATE TABLE t (columns)
PARTITION BY p_type(p_func(col1, col2,... colN)...);
再假設查詢的WHERE條件形式如下:
WHERE t.col1=const1 AND37. col2=const2 AND ... t.colN=constN
我們能計算出p_func(const1, const2 ... constN),並挖掘出哪個分區包含的記錄和WHERE條件一樣。注意:這個流程會在所有的分區類型和所有的分區函數上操作。
注意:此流程只工作在,如果WHERE條件的形式像上述那樣,表的每個列必需被驗證是否等與一些任意常數(不需要相同的常數為每列)。例如,如果上述例子的WHERE語句中沒有col1=const1,那麼我們不會計算p_func分區函數的值,也不會約束實際被用的分區集。
區間遊歷(Walking)
假設一個分區表t定義成columns列集,分區型別p_type,分區函數p_func使用integer型別類
,如下:
p_type(p_func(int_col))
...
假設我們有以下形式的WHERE條件查詢:
WHERE const1 我們能縮小此情況的條件成一系列單點間(Single-Single- Point Intervals),如下,透過轉換此WHERE語句為下列關係:
int_field=const1 OR
int_field=const1 + 1 OR OR
int_field=const2
PARTITION BY RANGE|LIST(unary_ascending_function
是以下形式中的一種:const1 t.column const1
自分區函數是升序,看如下的關係:
const
如範圍分區(RANGE partitioning),每個分區佔據一個區間於分區函數值的軸線上,每個區間是不相連的,如下:
p1 p2
table partitions ----- -x------x--------x-------->
search interval ----x============== x----------->
A B
一個分區需要被訪問,當且僅當如果它的區間和搜索區間[A, B]沒有空的交叉點。
如列舉分區(LIST partitioning),各分區包含點集於分區函數值的軸線上,各分區會產生不同的交叉點,如下:
p2 p1 p1 p0
table partitions - -+---+----+----+----+----+---->
search interval ----x===================x------>
一個分區需要被訪問,至少一個交叉點在搜尋區間[A, B]裡。所使用的分區集可確定運行從A到B,並收集它們的點在這個搜尋範圍內的分區。
子分區區間(subpartitioning intervals)
在前面部分我們描述幾種從基本的WHERE條件推斷出在用分區集。一切都表明,這些分區的推斷方法都適合子分區,除範圍分區(RANGE partitioning)和列舉分區(LIST partitioning)的子分區外。
自每個分區以相同的方式被分子分區,我們會找出在每個分區內的哪個子分區會被訪問。
從WHERE語句到區間(From WHERE Clauses to Intervals)
之前的章節講述了,從表示分區和子分區區間的WHERE語句推斷出分區集。現在我們來看看如何從任一WHERE語句抽出區間。
抽取的流程使用範圍分析器(RANGE Analyzer),屬於MySQL最佳化器的一部分,它產生範圍RANGE存取的計畫。這是因為這個任務是相似的。兩種WHERE語句的形式:RANGE存取類型使用索引範圍(區間)掃描;分區裁切(partition pruning)模組使用分區區間,用來決定哪個分區被使用。
為了分區裁剪(partition pruning),範圍分析器(RANGE Analyzer)與WHERE語句被調用,一個由分區和子分區函數使用的表的列清單:
(part_col1, part_col2, ... part_colN, part_colN, subpart_col1, subpart_col2, ... subpart_colM)
範圍分析器(RANGE Analyzer)工作的結果稱為SEL_ARG圖。這是一個很複雜的結構,我們不打算在這裡描述它。目前這個文化討論的重點是我們能遊歷所有分區,並收集分區和子分區的區間。
PARTITION BY LIST (pf)
SUBPARTITION BY HASH(sp1, sp2) (
PARTITION p0 VALUES IN (1),
PARTITION p1 VALUES IN (2),
PARTITION p2 VALUES IN (3),]
);
現假設對錶t的一個很複雜的WHERE語句查詢:
pf=1 AND (sp1='foo' AND sp2 IN (40,50))
OR
OR
p=8
(根)
| :
|分區: | :
-----+
---| pf=1 |----:-----| sp1='foo' |---| sp2=40 |
+------+ : +-----------+ +--------+
| : : +--------+
| sp2=50 |
| : +--------+
|
+------+ : +-----------+ +--------+
| pf=3 |----:--+--| sp1='吧台' |---| sp2=33 |
+--------+ : | +----- -------+ +--------+
| : |
+------+ +- ----+ :
| :
+-----------+
| pf=8 |----:----- | sp1='baz' |
+------+ : +-----------+
上述圖表,垂直的邊界(|)代表OR,橫的(-)代表AND ,的和垂直的線也代表AND。
分區修剪)代碼遊歷從圖上方到下方,從左橫到右邊,並做瞭如下的推
執行pf=1的區間分析,找出分區P0的對應集合,右邊到sp1='foo'
右移,到sp2=40
下移到sp2=50
分析sp1= 'foo'區間和sp2=50區間,在某SP2子分區找到行。推論二:在每個分區組成集合P0,標識子分區SP2「被使用」移回pf=1,然後下稱到pf =3
3。
執行pf=3的區間分析,找到分區P1的對應集合,右移到sp1='bar'
再右移,到sp2=33
分析sp1='bar' AND sp2= 33區間,在某某SP3子分割區找到行。推論三:在每個分區組成集合P1,標識子分區SP3「被使用」
移回pf=3,然後下移到pf=4
4。
執行pf=4的區間分析,找到分區P2的相應集合,右移到sp2='bar'
移回pf=3,然後下稱到pf=8
執行pf =8的區間分析,找出分區P3的對應集合,右移到sp1='baz'
現在到了sp1='baz',發現不能再向右移動,也不能建構子分區區間。我們記錄下,並返回pf=8
從之前的過程,我們不能再限制子分區區間了,所以推論:在P3分區集裡的每個分區,假設所有的子分區都是有效在用的。
6。試著從pf=9下移,發現到尾,所以遊歷圖也就完成。
注意:在特定的情況下,範圍分析器(RANGE Analyzer)的結果會有幾種的SEL_ARG圖,這圖是由OR或AND操作符組成的。出現這種情況對於WHERE語句,要么是非常複雜的要么是一個單一的區間列表建構。對這種情況,分區裁剪(partition pruning)代碼採用適當的操作,例:
SELECT * FROM t1 WHERE partition_id=10 OR subpartition_id=20
在這個實例中,沒有單一的區間被構建,但分區(partition pruning)程式碼正確地推斷了使用的分區集是聯合:
所有在分區裡的子分區包含了partition_id=10的行,在每個分區裡一個子分區包含subpartition_id=20的行。
原始程式碼中分區裁切(partition pruning)實作
原始程式碼的簡單解說:
sql/opt_range.cc:
這程式碼包含了從WHERWHER ,方法prune_partitions()。關於分區裁切(partition pruning)的都有詳細的行行程式碼註釋,從PartitionPruningModule程式碼開始:
sql/partition_info.h:
class partition_info {
...
/*
class partition_info {
...
/*
. away) 部分。 on this partitioned table (used by the code in opt_range .cc)
get_partitions_in_range_iter get_part_iter_for_interval;
get_partitions_in_range_iter get_subpart_iter_for_interval; 。
分區檢索
如果分區表被一系列索引檢索(即ref,eq_ref,ref_or_null連接存取方式)訪問,MySQL會檢查是否需要所有分區做索引檢索或限制訪問到特定的分區。例:
keypart2 INT,
KEY(keypart1, keypart2))
PARTITION BY HASH(keypart2);INSERT INTO t2 VALUES (1,1),(2,2),(3,3);如下:SELECT * FROM t1, t2 WHERE t2.keypart1=t1.a
AND t2.keypart2=t1.b;
利用如下算法執行:
(for each record in t1:)
t2 ->index_read({current-value-of(t1.a), current-value-of(t1.b)});
while( t2->index_next_same() )pass row combination to query output; 在index_read()呼叫中,分區表句柄會挖掘出被確定所有分區列的值,在這個例子中,是單一列b,接著找出一個分區存取。如果這個分區被裁剪過,就沒其它的分區可訪問。
以上就是MySQL Internals Optimizer的內容,更多相關文章請關注PHP中文網(www.php.cn)!