本文檔主要介紹了PolarDB MySQL版如何通過CBQT組件(Cost Based Query Transformation)實現基於代價的查詢變換,從而大幅提升複雜查詢的執行效率。
前提條件
查詢變換舊版
叢集版本需為PolarDB MySQL版8.0.2版本且Revision version為8.0.2.2.0及以上。
查詢變換新版
叢集版本需為PolarDB MySQL版8.0.2版本且Revision version為8.0.2.2.19及以上。
您可以通過查詢版本號碼來確認叢集版本。
背景資訊
查詢變換是指基於等價規則,將一個查詢語句改寫成語義上等價的另一種形式。如以下查詢語句:
SELECT *
FROM d1
JOIN f1 ON d1.c1 = f1.c1
LEFT JOIN (
SELECT d2.c2 AS d2_c2, f2.c3 AS f2_c3
FROM d2, f2
WHERE d2.c1 = f2.c1
) derived
ON derived.d2_c2 = d1.c2
AND derived.f2_c3 = f1.c3;
以上查詢語句可以通過物化表合并規則將物化表展開,展開後改寫為如下查詢語句:
SELECT *
FROM d1
JOIN f1 ON d1.c1 = f1.c1
LEFT JOIN (d2
JOIN f2 ON TRUE)
ON d2.c1 = f2.c1
AND f2.c3 = f1.c3
AND d2.c2 = d1.c2;
社區版MySQL目前只支援基於規則的查詢變換,語義上能做的變換肯定會做,比如上述變換在社區版MySQL中肯定會進行。但實際上這些變換不一定是更優的,比如上述查詢語句,如果d1
、f1
與d2
、f2
之間沒有索引關係,d1
和f1
串連輸出的每一行都會驅動d2
和f2
做一次串連的重複計算,執行效率會大大降低。所以,某些查詢語句需要根據執行代價來決定是否需要做查詢變換,特別是複雜的變換,其往往會受到Cardinality、Access Method、Join Order等多種因素的影響。基於此,PolarDB MySQL版實現了基於代價的查詢變換,能夠基於執行代價選擇是否做某種變換。
對於複雜查詢,CBQT會收集該查詢都可以做哪些基於代價的查詢變換,這些變換會匯總成一個狀態空間。CBQT會在該狀態空間中選擇產生執行計畫代價最低的狀態,執行對應的計劃。如下圖所示,對於輸入的SQL語句,CBQT收集到基於代價的查詢變換A和B,這兩個變換組成的狀態空間有:None(均不作變換)、A(只做變換A)、B(只做變換B)、AB(變換A和變換B都做)。這些狀態空間分別對應不同的執行計畫:Plan1、Plan2、Plan3、Plan4。CBQT會從中選擇最優的執行計畫,圖中選擇了Plan2,即查詢變換A。同時,那些肯定能帶來收益的變換被定義為基於規則的查詢變換並被添加在架構中,如果符合變換規則,這些變換肯定會被調用。
以上述的查詢語句為例,其經過物化表合并變換後形成的執行計畫如下:
如果不做物化表合并,其執行計畫如下:
這兩種計劃哪種更優取決於d2
、f2
表與d1
、f2
表之間是否有索引關聯以及串連結果集大小。如果有索引關聯關係,則合并計劃可能更優。否則,不合并更優。CBQT架構會計算合并與不合并的代價並進行對比,最終選擇一個適合當前資料情境下的更優的計劃。
名詞解釋
查詢嵌套及嵌套深度
查詢嵌套指的是一個查詢語塊嵌套在另一個查詢語句中的情況,前者為子查詢或者內查詢,後者為父查詢或外查詢。主要針對子查詢和物化表的情況,嵌套深度為查詢塊嵌套的層數。樣本如下:
SELECT * FROM t1 WHERE t1.a IN (SELECT t2.b FROM t2 WHERE t2.c=t1.b);
其嵌套深度為2,父查詢為查詢t1
表的查詢語句,子查詢為查詢t2
表的查詢語句。
需要注意的是,union查詢是在同一層的SQL語句。樣本如下:
SELECT *
FROM t1
WHERE t1.a IN (
SELECT dt.b
FROM (
SELECT b, c
FROM t2
UNION
SELECT b, c
FROM t3
) dt
WHERE dt.c = t1.b
);
以上查詢語句的嵌套深度為3,dt
表中的子查詢是由union組合而成,屬於同一層查詢。
變換
基於等價規則將查詢轉換為另一種形式。例如背景資訊一節中的物化表合并。
變換對象
等價規則應用的對象。如背景資訊一節中物化表合并樣本中的derived表。不同的變換規則對應不同的變換對象,例如,物化表合并的變換對象為物化表,而子查詢轉Semi Join中的變換對象為子查詢。
迭代
變換流程迴圈的次數。CBQT的變換流程會對所有對象上所有的可能變換做嘗試,但在某些變換上做完嘗試後,在某些查詢塊上可能會出現新的可以做變換的規則。例如,子查詢轉為物化表後就可以應用物化表合并規則。因此,需要對新產生的對象繼續調用變換流程,即CBQT流程中的迭代。迭代次數是指反覆調用變換流程的次數。
使用方法
基於代價的查詢變換(CBQT)功能一直在演化,目前已經有了兩個版本的CBQT能力,基於代價的查詢變換功能的總開關是由參數cbqt_enabled
控制。使用哪種版本的查詢變換是由參數cbqt_version
控制,當參數cbqt_version
的值為1時,表示使用查詢變換舊版。當參數cbqt_version
的值為2時,表示使用查詢變換新版。
參數名稱 | 層級 | 描述 |
cbqt_enabled | Global、Session | 基於代價的查詢變換控制開關。取值範圍如下:
|
cbqt_version | Global、Session | CBQT版本控制開關。取值範圍如下:
|
兩個版本的詳細參數使用說明如下:
查詢變換舊版
由於基於代價的查詢變換會增加執行計畫的搜尋空間,同時會增加一定的最佳化搜尋時間。為了避免對短查詢造成影響,您可以通過參數cbqt_cost_threshold
來配置基於代價查詢變換的代價閾值,只有執行代價超過該閾值的查詢語句才會考慮使用基於代價的查詢變換。對於PolarDB新加的子查詢(MySQL社區沒有的)使用GROUP BY解關聯變換,該變換完全是基於代價的查詢變換。對於MySQL社區已有的變換,您可以通過參數控制其是否基於代價選取查詢變換。對於derived merge,您可以通過參數polar_optimizer_switch
中的開關derived_merge_cost_based
來選擇其是否受基於代價的查詢變換控制。
參數名稱 | 層級 | 描述 |
cbqt_cost_threshold | Global、Session | 當查詢語句的原執行代價超過該值時,對查詢語句使用基於代價的查詢變換。 取值範圍:0~18446744073709551615。預設值為100000。 |
cbqt_timeout | Global、Session | 為避免搜尋合適的查詢變換花費太多的最佳化時間,當最佳化時間超過該值時,將不再繼續搜尋。 取值範圍:0~18446744073709551615。預設值為200。單位為毫秒。 說明 該值為0時,表示搜尋時間無限制。 |
polar_optimizer_switch | Global、Session | 查詢最佳化控制開關。取值範圍如下:
|
查詢變換新版
基於代價的查詢變換需要反覆迭代以搜尋到最優執行計畫,這可能會增加最佳化時間。因此,您可以通過以下參數來控制搜尋的過程。
參數名稱 | 層級 | 描述 |
cbqt_iteration_limit | Global | CBQT迭代次數。迭代次數越多選擇出最優計劃的可能性越大,但最佳化時間會更長。反之選出最優計劃的可能性越小,最佳化時間更短。 取值範圍:1~10。預設值為1。單位為次。 |
cbqt_max_nested_level | Global、Session | CBQT處理的查詢語句最大嵌套深度。如果查詢語句的嵌套深度超過該值,將不會對該查詢語句使用CBQT。 取值範圍:1~64。預設值為5。 |
cbqt_search_strategy | Global | CBQT搜尋最優計劃的策略。取值範圍如下:
|
cbqt_rule_switch | Global、Session | 查詢最佳化控制開關。該參數控制是否使用某種查詢變換。取值範圍如下:
|