控制優化器策略的一種方法是設定 optimizer_switch
系統變數(請參閱第 10.9.2 節,「可切換的最佳化」)。變更此變數會影響所有後續查詢的執行;若要使一個查詢與另一個查詢有不同的影響,必須在每個查詢之前變更 optimizer_switch
。
控制優化器的另一種方法是使用優化器提示,這些提示可以在個別語句中指定。由於優化器提示適用於每個語句的基礎,因此它們提供的語句執行計畫控制比使用 optimizer_switch
更好。例如,您可以在一個語句中啟用一個表格的最佳化,並在另一個表格中停用該最佳化。語句中的提示優先於 optimizer_switch
旗標。
範例
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
FROM t3 WHERE f1 > 30 AND f1 < 33;
SELECT /*+ BKA(t1) NO_BKA(t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ NO_ICP(t1, t2) */ * FROM t1 INNER JOIN t2 WHERE ...;
SELECT /*+ SEMIJOIN(FIRSTMATCH, LOOSESCAN) */ * FROM t1 ...;
EXPLAIN SELECT /*+ NO_ICP(t1) */ * FROM t1 WHERE ...;
SELECT /*+ MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
此處描述的優化器提示與 第 10.9.4 節,「索引提示」中描述的索引提示不同。優化器和索引提示可以單獨使用或一起使用。
優化器提示適用於不同的範圍層級
全域:提示會影響整個語句
查詢區塊:提示會影響語句內的特定查詢區塊
表格層級:提示會影響查詢區塊內的特定表格
索引層級:提示會影響表格內的特定索引
下表總結了可用的最佳化器提示、它們影響的最佳化器策略,以及它們適用的範圍。 稍後將提供更多詳細資訊。
表 10.2 可用的最佳化器提示
提示名稱 | 描述 | 適用範圍 |
---|---|---|
BKA 、NO_BKA |
影響批次索引鍵存取聯結處理 | 查詢區塊、資料表 |
BNL 、NO_BNL |
影響雜湊聯結最佳化 | 查詢區塊、資料表 |
DERIVED_CONDITION_PUSHDOWN 、NO_DERIVED_CONDITION_PUSHDOWN |
針對實體化衍生表使用或忽略衍生條件下推最佳化 | 查詢區塊、資料表 |
GROUP_INDEX 、NO_GROUP_INDEX |
針對 GROUP BY 操作中的索引掃描使用或忽略指定的索引 |
索引 |
HASH_JOIN 、NO_HASH_JOIN |
影響雜湊聯結最佳化 (在 MySQL 9.0 中無效) | 查詢區塊、資料表 |
INDEX 、NO_INDEX |
作為 JOIN_INDEX 、GROUP_INDEX 和 ORDER_INDEX 的組合,或作為 NO_JOIN_INDEX 、NO_GROUP_INDEX 和 NO_ORDER_INDEX 的組合 |
索引 |
INDEX_MERGE 、NO_INDEX_MERGE |
影響索引合併最佳化 | 資料表、索引 |
JOIN_FIXED_ORDER |
使用 FROM 子句中指定的資料表順序作為聯結順序 |
查詢區塊 |
JOIN_INDEX 、NO_JOIN_INDEX |
針對任何存取方法使用或忽略指定的索引 | 索引 |
JOIN_ORDER |
使用提示中指定的資料表順序作為聯結順序 | 查詢區塊 |
JOIN_PREFIX |
使用提示中指定的資料表順序作為聯結順序的第一個資料表 | 查詢區塊 |
JOIN_SUFFIX |
使用提示中指定的資料表順序作為聯結順序的最後一個資料表 | 查詢區塊 |
MAX_EXECUTION_TIME |
限制語句執行時間 | 全域 |
MERGE 、NO_MERGE |
影響衍生表/檢視表合併到外部查詢區塊 | 資料表 |
MRR 、NO_MRR |
影響多範圍讀取最佳化 | 資料表、索引 |
NO_ICP |
影響索引條件下推最佳化 | 資料表、索引 |
NO_RANGE_OPTIMIZATION |
影響範圍最佳化 | 資料表、索引 |
ORDER_INDEX 、NO_ORDER_INDEX |
使用或忽略指定的索引進行資料列排序 | 索引 |
QB_NAME |
為查詢區塊指派名稱 | 查詢區塊 |
RESOURCE_GROUP |
在語句執行期間設定資源群組 | 全域 |
SEMIJOIN 、NO_SEMIJOIN |
影響半聯結和反聯結策略 | 查詢區塊 |
SKIP_SCAN 、NO_SKIP_SCAN |
影響跳過掃描最佳化 | 資料表、索引 |
SET_VAR |
在語句執行期間設定變數 | 全域 |
SUBQUERY |
影響具體化、IN 到 EXISTS 子查詢策略 |
查詢區塊 |
停用最佳化會阻止最佳化器使用它。 啟用最佳化意味著最佳化器可以自由使用該策略 (如果它適用於語句執行),而不是最佳化器必須使用它。
MySQL 支援 SQL 語句中的註解,如第 11.7 節,「註解」所述。 最佳化器提示必須在 /*+ ... */
註解中指定。 也就是說,最佳化器提示使用 /* ... */
C 樣式註解語法的變體,在 /*
註解開啟序列之後加上 +
字元。 範例
/*+ BKA(t1) */
/*+ BNL(t1, t2) */
/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */
/*+ QB_NAME(qb2) */
在 +
字元之後允許空白字元。
剖析器會識別 SELECT
、UPDATE
、INSERT
、REPLACE
和 DELETE
陳述式的初始關鍵字之後的最佳化器提示註解。 在這些情況下允許提示
在查詢和資料變更陳述式的開頭
SELECT /*+ ... */ ... INSERT /*+ ... */ ... REPLACE /*+ ... */ ... UPDATE /*+ ... */ ... DELETE /*+ ... */ ...
在查詢區塊的開頭
(SELECT /*+ ... */ ... ) (SELECT ... ) UNION (SELECT /*+ ... */ ... ) (SELECT /*+ ... */ ... ) UNION (SELECT /*+ ... */ ... ) UPDATE ... WHERE x IN (SELECT /*+ ... */ ...) INSERT ... SELECT /*+ ... */ ...
在以
EXPLAIN
作為開頭的可提示陳述式中。 例如EXPLAIN SELECT /*+ ... */ ... EXPLAIN UPDATE ... WHERE x IN (SELECT /*+ ... */ ...)
其含義是您可以使用
EXPLAIN
來查看最佳化器提示如何影響執行計畫。 在EXPLAIN
之後立即使用SHOW WARNINGS
來查看如何使用提示。 後續SHOW WARNINGS
顯示的擴充EXPLAIN
輸出會指出哪些提示已使用。 忽略的提示不會顯示。
一個提示註解可以包含多個提示,但一個查詢區塊不能包含多個提示註解。 這是有效的
SELECT /*+ BNL(t1) BKA(t2) */ ...
但這是無效的
SELECT /*+ BNL(t1) */ /* BKA(t2) */ ...
當提示註解包含多個提示時,可能會出現重複和衝突。 適用以下一般準則。 針對特定提示類型,可能適用其他規則,如提示描述中所述。
重複的提示:對於像
/*+ MRR(idx1) MRR(idx1) */
這樣的提示,MySQL 會使用第一個提示,並針對重複的提示發出警告。衝突的提示:對於像
/*+ MRR(idx1) NO_MRR(idx1) */
這樣的提示,MySQL 會使用第一個提示,並針對第二個衝突的提示發出警告。
查詢區塊名稱是識別碼,並遵循關於哪些名稱有效以及如何引用它們的常見規則 (請參閱第 11.2 節,「結構描述物件名稱」)。
提示名稱、查詢區塊名稱和策略名稱不區分大小寫。 對於資料表和索引名稱的參照遵循常見的識別碼區分大小寫規則 (請參閱第 11.2.3 節,「識別碼區分大小寫」)。
聯結順序提示會影響最佳化器聯結資料表的順序。
JOIN_FIXED_ORDER
提示的語法
hint_name([@query_block_name])
其他聯結順序提示的語法
hint_name([@query_block_name] tbl_name [, tbl_name] ...)
hint_name(tbl_name[@query_block_name] [, tbl_name[@query_block_name]] ...)
此語法參照下列術語
hint_name
:允許使用這些提示名稱JOIN_FIXED_ORDER
:強制最佳化器使用它們在FROM
子句中出現的順序聯結資料表。 這與指定SELECT STRAIGHT_JOIN
相同。JOIN_ORDER
:指示最佳化器使用指定的資料表順序聯結資料表。 此提示適用於已命名的資料表。 最佳化器可能會將未命名的資料表放置在聯結順序中的任何位置,包括在指定的資料表之間。JOIN_PREFIX
:指示最佳化器使用指定的資料表順序聯結聯結執行計畫的第一個資料表。 此提示適用於已命名的資料表。 最佳化器會將所有其他資料表放置在已命名的資料表之後。JOIN_SUFFIX
:指示最佳化器使用指定的資料表順序聯結聯結執行計畫的最後一個資料表。 此提示適用於已命名的資料表。 最佳化器會將所有其他資料表放置在已命名的資料表之前。
tbl_name
:語句中使用的資料表名稱。 命名資料表的提示適用於它命名的所有資料表。JOIN_FIXED_ORDER
提示不命名任何資料表,並適用於發生它的查詢區塊FROM
子句中的所有資料表。如果資料表有別名,則提示必須參照別名,而不是資料表名稱。
提示中的資料表名稱不能使用結構描述名稱來限定。
query_block_name
:提示適用的查詢區塊。 如果提示不包含前導@
,則提示適用於它所發生的查詢區塊。 對於query_block_name
語法,提示適用於已命名查詢區塊中已命名的資料表。 若要為查詢區塊指派名稱,請參閱用於命名查詢區塊的最佳化器提示。tbl_name
@query_block_name
範例
SELECT
/*+ JOIN_PREFIX(t2, t5@subq2, t4@subq1)
JOIN_ORDER(t4@subq1, t3)
JOIN_SUFFIX(t1) */
COUNT(*) FROM t1 JOIN t2 JOIN t3
WHERE t1.f1 IN (SELECT /*+ QB_NAME(subq1) */ f1 FROM t4)
AND t2.f1 IN (SELECT /*+ QB_NAME(subq2) */ f1 FROM t5);
提示會控制合併到外部查詢區塊的半聯結資料表的行為。 如果子查詢 subq1
和 subq2
轉換為半聯結,則資料表 t4@subq1
和 t5@subq2
會合併到外部查詢區塊。 在此情況下,外部查詢區塊中指定的提示會控制 t4@subq1
、t5@subq2
資料表的行為。
最佳化器會根據下列原則解析聯結順序提示
多個提示執行個體
每種類型的
JOIN_PREFIX
和JOIN_SUFFIX
提示只會套用一個。 任何相同類型的後續提示都會被忽略並發出警告。JOIN_ORDER
可以指定多次。範例
/*+ JOIN_PREFIX(t1) JOIN_PREFIX(t2) */
第二個
JOIN_PREFIX
提示會被忽略並發出警告。/*+ JOIN_PREFIX(t1) JOIN_SUFFIX(t2) */
兩種提示都適用。不會發生警告。
/*+ JOIN_ORDER(t1, t2) JOIN_ORDER(t2, t3) */
兩種提示都適用。不會發生警告。
衝突的提示
在某些情況下,提示可能會衝突,例如當
JOIN_ORDER
和JOIN_PREFIX
具有無法同時套用的表格順序時。SELECT /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */ ... FROM t1, t2;
在這種情況下,會套用第一個指定的提示,而後續衝突的提示會被忽略,且不發出警告。無法套用的有效提示會被靜默忽略,且不發出警告。
被忽略的提示
如果提示中指定的表格有循環相依性,則該提示會被忽略。
範例
/*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */
JOIN_ORDER
提示設定表格t2
依賴於t1
。JOIN_PREFIX
提示會被忽略,因為表格t1
不能依賴於t2
。被忽略的提示不會顯示在擴展的EXPLAIN
輸出中。與
const
表格的互動MySQL 優化器將
const
表格放在聯接順序的最前面,而const
表格的位置不受提示的影響。聯接順序提示中對const
表格的引用會被忽略,但提示仍然適用。例如,以下兩者是等效的:JOIN_ORDER(t1, const_tbl, t2) JOIN_ORDER(t1, t2)
擴展
EXPLAIN
輸出中顯示的接受提示包括const
表格,因為它們是被指定的那樣。與聯接操作類型的互動
MySQL 支援幾種聯接類型:
LEFT
、RIGHT
、INNER
、CROSS
、STRAIGHT_JOIN
。與指定聯接類型衝突的提示會被忽略,且不發出警告。範例
SELECT /*+ JOIN_PREFIX(t1, t2) */FROM t2 LEFT JOIN t1;
這裡提示中要求的聯接順序與
LEFT JOIN
所要求的順序之間發生衝突。提示會被忽略,且不發出警告。
表格級別的提示會影響
區塊巢狀迴圈 (BNL) 和批次金鑰存取 (BKA) 聯接處理演算法的使用(請參閱第 10.2.1.12 節,〈區塊巢狀迴圈和批次金鑰存取聯接〉)。
衍生表格、視圖參考或通用表格運算式是否應合併到外部查詢區塊中,或使用內部臨時表格實體化。
衍生表格條件下推優化的使用。請參閱第 10.2.2.5 節,〈衍生條件下推優化〉。
這些提示類型適用於特定表格,或查詢區塊中的所有表格。
表格級別提示的語法
hint_name([@query_block_name] [tbl_name [, tbl_name] ...])
hint_name([tbl_name@query_block_name [, tbl_name@query_block_name] ...])
此語法參照下列術語
hint_name
:允許使用這些提示名稱DERIVED_CONDITION_PUSHDOWN
、NO_DERIVED_CONDITION_PUSHDOWN
:啟用或停用指定表格的衍生表格條件下推。如需更多資訊,請參閱第 10.2.2.5 節,〈衍生條件下推優化〉。HASH_JOIN
、NO_HASH_JOIN
:這些提示在 MySQL 9.0 中沒有作用;請改用BNL
或NO_BNL
。MERGE
、NO_MERGE
:啟用指定表格、視圖參考或通用表格運算式的合併;或停用合併並改用實體化。
注意若要使用區塊巢狀迴圈或批次金鑰存取提示來為外部聯接的任何內部表格啟用聯接緩衝,則必須為外部聯接的所有內部表格啟用聯接緩衝。
tbl_name
:陳述式中使用的表格名稱。提示適用於它命名的所有表格。如果提示沒有命名任何表格,則適用於其所在的查詢區塊的所有表格。如果資料表有別名,則提示必須參照別名,而不是資料表名稱。
提示中的資料表名稱不能使用結構描述名稱來限定。
query_block_name
:提示適用的查詢區塊。 如果提示不包含前導@
,則提示適用於它所發生的查詢區塊。 對於query_block_name
語法,提示適用於已命名查詢區塊中已命名的資料表。 若要為查詢區塊指派名稱,請參閱用於命名查詢區塊的最佳化器提示。tbl_name
@query_block_name
範例
SELECT /*+ NO_BKA(t1, t2) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
SELECT /*+ NO_BNL() BKA(t1) */ t1.* FROM t1 INNER JOIN t2 INNER JOIN t3;
SELECT /*+ NO_MERGE(dt) */ * FROM (SELECT * FROM t1) AS dt;
表格級別提示適用於從先前表格接收記錄的表格,而不是傳送者表格。考慮這個陳述式:
SELECT /*+ BNL(t2) */ FROM t1, t2;
如果優化器選擇先處理 t1
,它會透過緩衝來自 t1
的資料列,然後才開始從 t2
讀取,來對 t2
套用區塊巢狀迴圈聯接。如果優化器改為選擇先處理 t2
,則提示沒有作用,因為 t2
是傳送者表格。
對於 MERGE
和 NO_MERGE
提示,以下優先順序規則適用:
提示的優先順序高於任何非技術約束的優化器啟發式演算法。(如果提供提示作為建議沒有效果,則優化器會有忽略它的理由。)
提示的優先順序高於
derived_merge
系統變數的optimizer_switch
旗標。對於視圖參考,視圖定義中的
ALGORITHM={MERGE|TEMPTABLE}
子句的優先順序高於在引用視圖的查詢中指定的提示。
索引級別的提示會影響優化器針對特定表格或索引使用的索引處理策略。這些提示類型會影響索引條件下推 (ICP)、多範圍讀取 (MRR)、索引合併和範圍優化(請參閱第 10.2.1 節,〈優化 SELECT 陳述式〉)。
索引級別提示的語法
hint_name([@query_block_name] tbl_name [index_name [, index_name] ...])
hint_name(tbl_name@query_block_name [index_name [, index_name] ...])
此語法參照下列術語
hint_name
:允許使用這些提示名稱GROUP_INDEX
、NO_GROUP_INDEX
:啟用或停用指定索引或索引,以用於GROUP BY
作業的索引掃描。等同於索引提示FORCE INDEX FOR GROUP BY
、IGNORE INDEX FOR GROUP BY
。INDEX
、NO_INDEX
:充當JOIN_INDEX
、GROUP_INDEX
和ORDER_INDEX
的組合,強制伺服器在任何和所有範圍內使用指定的索引或索引;或充當NO_JOIN_INDEX
、NO_GROUP_INDEX
和NO_ORDER_INDEX
的組合,這會導致伺服器在任何和所有範圍內忽略指定的索引或索引。等同於FORCE INDEX
、IGNORE INDEX
。INDEX_MERGE
、NO_INDEX_MERGE
:啟用或停用指定表格或索引的索引合併存取方法。如需此存取方法的資訊,請參閱第 10.2.1.3 節,〈索引合併優化〉。這些提示適用於所有三個索引合併演算法。INDEX_MERGE
提示強制優化器對指定的表格使用索引合併,並使用指定的索引集。如果未指定索引,優化器會考慮所有可能的索引組合,並選擇成本最低的一個。如果索引組合不適用於給定的陳述式,則可能會忽略提示。NO_INDEX_MERGE
提示會停用任何涉及指定索引的索引合併組合。如果提示沒有指定索引,則不允許該表格使用索引合併。JOIN_INDEX
、NO_JOIN_INDEX
:強制 MySQL 對任何存取方法(例如ref
、range
、index_merge
等)使用或忽略指定的索引或索引。等同於FORCE INDEX FOR JOIN
、IGNORE INDEX FOR JOIN
。MRR
、NO_MRR
:啟用或停用指定表格或索引的 MRR。MRR 提示僅適用於InnoDB
和MyISAM
表格。如需此存取方法的資訊,請參閱第 10.2.1.11 節,〈多範圍讀取優化〉。NO_ICP
:停用指定表格或索引的 ICP。預設情況下,ICP 是一種候選優化策略,因此沒有啟用它的提示。如需此存取方法的資訊,請參閱第 10.2.1.6 節,〈索引條件下推優化〉。NO_RANGE_OPTIMIZATION
:停用指定表格或索引的索引範圍存取。此提示也會停用表格或索引的索引合併和鬆散索引掃描。依預設,範圍存取是候選的最佳化策略,因此沒有啟用它的提示。當範圍數量可能很多,且範圍最佳化會需要許多資源時,此提示可能很有用。
ORDER_INDEX
、NO_ORDER_INDEX
:使 MySQL 使用或忽略指定的索引來排序資料列。相當於FORCE INDEX FOR ORDER BY
、IGNORE INDEX FOR ORDER BY
。SKIP_SCAN
、NO_SKIP_SCAN
:啟用或停用指定表格或索引的跳過掃描存取方法。有關此存取方法的資訊,請參閱 跳過掃描範圍存取方法。SKIP_SCAN
提示會強制最佳化工具針對指定的表格使用跳過掃描,並使用指定的索引集。如果沒有指定索引,最佳化工具會考量所有可能的索引,並選擇成本最低的索引。如果索引不適用於給定的陳述式,則可能會忽略此提示。NO_SKIP_SCAN
提示會停用指定索引的跳過掃描。如果提示未指定索引,則不允許表格使用跳過掃描。
tbl_name
:提示所套用的表格。index_name
:命名表格中索引的名稱。提示會套用至其命名的所有索引。如果提示未命名任何索引,則會套用至表格中的所有索引。若要參照主索引鍵,請使用名稱
PRIMARY
。若要查看表格的索引名稱,請使用SHOW INDEX
。query_block_name
:提示適用的查詢區塊。 如果提示不包含前導@
,則提示適用於它所發生的查詢區塊。 對於query_block_name
語法,提示適用於已命名查詢區塊中已命名的資料表。 若要為查詢區塊指派名稱,請參閱用於命名查詢區塊的最佳化器提示。tbl_name
@query_block_name
範例
SELECT /*+ INDEX_MERGE(t1 f3, PRIMARY) */ f2 FROM t1
WHERE f1 = 'o' AND f2 = f3 AND f3 <= 4;
SELECT /*+ MRR(t1) */ * FROM t1 WHERE f2 <= 3 AND 3 <= f3;
SELECT /*+ NO_RANGE_OPTIMIZATION(t3 PRIMARY, f2_idx) */ f1
FROM t3 WHERE f1 > 30 AND f1 < 33;
INSERT INTO t3(f1, f2, f3)
(SELECT /*+ NO_ICP(t2) */ t2.f1, t2.f2, t2.f3 FROM t1,t2
WHERE t1.f1=t2.f1 AND t2.f2 BETWEEN t1.f1
AND t1.f2 AND t2.f2 + 1 >= t1.f1 + 1);
SELECT /*+ SKIP_SCAN(t1 PRIMARY) */ f1, f2
FROM t1 WHERE f2 > 40;
以下範例使用索引合併提示,但其他索引層級提示遵循相同的原則,關於提示忽略以及最佳化工具提示相對於 optimizer_switch
系統變數或索引提示的優先順序。
假設表格 t1
有欄位 a
、b
、c
和 d
;且在 a
、b
和 c
上存在名為 i_a
、i_b
和 i_c
的索引
SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
WHERE a = 1 AND b = 2 AND c = 3 AND d = 4;
在此情況下,索引合併用於 (i_a, i_b, i_c)
。
SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c)*/ * FROM t1
WHERE b = 1 AND c = 2 AND d = 3;
在此情況下,索引合併用於 (i_b, i_c)
。
/*+ INDEX_MERGE(t1 i_a, i_b) NO_INDEX_MERGE(t1 i_b) */
由於有針對相同表格的先前提示,因此 NO_INDEX_MERGE
會被忽略。
/*+ NO_INDEX_MERGE(t1 i_a, i_b) INDEX_MERGE(t1 i_b) */
由於有針對相同表格的先前提示,因此 INDEX_MERGE
會被忽略。
對於 INDEX_MERGE
和 NO_INDEX_MERGE
最佳化工具提示,這些優先順序規則適用
如果指定了最佳化工具提示且適用,則它會優先於
optimizer_switch
系統變數中與索引合併相關的旗標。SET optimizer_switch='index_merge_intersection=off'; SELECT /*+ INDEX_MERGE(t1 i_b, i_c) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
此提示優先於
optimizer_switch
。在此情況下,索引合併用於(i_b, i_c)
。SET optimizer_switch='index_merge_intersection=on'; SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
此提示僅指定一個索引,因此不適用,並且適用
optimizer_switch
旗標 (on
)。如果最佳化工具評估其為符合成本效益,則會使用索引合併。SET optimizer_switch='index_merge_intersection=off'; SELECT /*+ INDEX_MERGE(t1 i_b) */ * FROM t1 WHERE b = 1 AND c = 2 AND d = 3;
此提示僅指定一個索引,因此不適用,並且適用
optimizer_switch
旗標 (off
)。不使用索引合併。索引層級最佳化工具提示
GROUP_INDEX
、INDEX
、JOIN_INDEX
和ORDER_INDEX
都會優先於等效的FORCE INDEX
提示;也就是說,它們會導致FORCE INDEX
提示被忽略。同樣地,NO_GROUP_INDEX
、NO_INDEX
、NO_JOIN_INDEX
和NO_ORDER_INDEX
提示都會優先於任何IGNORE INDEX
等效項,也會導致它們被忽略。索引層級最佳化工具提示
GROUP_INDEX
、NO_GROUP_INDEX
、INDEX
、NO_INDEX
、JOIN_INDEX
、NO_JOIN_INDEX
、ORDER_INDEX
和NO_ORDER_INDEX
提示都會優先於所有其他最佳化工具提示,包括其他索引層級最佳化工具提示。任何其他最佳化工具提示僅會套用至這些提示允許的索引。GROUP_INDEX
、INDEX
、JOIN_INDEX
和ORDER_INDEX
提示都相當於FORCE INDEX
,而不是USE INDEX
。這是因為使用其中一個或多個提示表示只有在無法使用其中一個已命名的索引來尋找表格中的資料列時,才會使用表格掃描。若要讓 MySQL 使用與給定USE INDEX
執行個體相同的索引或索引集,您可以使用NO_INDEX
、NO_JOIN_INDEX
、NO_GROUP_INDEX
、NO_ORDER_INDEX
或這些的某種組合。若要複製
USE INDEX
在查詢SELECT a,c FROM t1 USE INDEX FOR ORDER BY (i_a) ORDER BY a
中的效果,您可以使用NO_ORDER_INDEX
最佳化工具提示來涵蓋表格上的所有索引,但想要的索引除外,如下所示SELECT /*+ NO_ORDER_INDEX(t1 i_b,i_c) */ a,c FROM t1 ORDER BY a;
嘗試將整個表格的
NO_ORDER_INDEX
與USE INDEX FOR ORDER BY
結合無法達到此目的,因為NO_ORDER_BY
會導致忽略USE INDEX
,如下所示mysql> EXPLAIN SELECT /*+ NO_ORDER_INDEX(t1) */ a,c FROM t1 -> USE INDEX FOR ORDER BY (i_a) ORDER BY a\G *************************** 1. row *************************** id: 1 select_type: SIMPLE table: t1 partitions: NULL type: ALL possible_keys: NULL key: NULL key_len: NULL ref: NULL rows: 256 filtered: 100.00 Extra: Using filesort
USE INDEX
、FORCE INDEX
和IGNORE INDEX
索引提示的優先順序高於INDEX_MERGE
和NO_INDEX_MERGE
最佳化工具提示。/*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ ... IGNORE INDEX i_a
IGNORE INDEX
優先於INDEX_MERGE
,因此索引i_a
會從索引合併的可能範圍中排除。/*+ NO_INDEX_MERGE(t1 i_a, i_b) */ ... FORCE INDEX i_a, i_b
由於
FORCE INDEX
的緣故,不允許i_a, i_b
進行索引合併,但會強制最佳化工具針對range
或ref
存取使用i_a
或i_b
。沒有衝突;兩個提示都適用。如果
IGNORE INDEX
提示命名了多個索引,則這些索引無法用於索引合併。FORCE INDEX
和USE INDEX
提示只會使已命名的索引可用於索引合併。SELECT /*+ INDEX_MERGE(t1 i_a, i_b, i_c) */ a FROM t1 FORCE INDEX (i_a, i_b) WHERE c = 'h' AND a = 2 AND b = 'b';
索引合併交集存取演算法用於
(i_a, i_b)
。如果將FORCE INDEX
變更為USE INDEX
,則情況也是如此。
子查詢提示會影響是否使用半聯結轉換,以及允許的半聯結策略,並且在不使用半聯結時,是否使用子查詢具體化或 IN
至 EXISTS
轉換。有關這些最佳化的詳細資訊,請參閱 第 10.2.2 節,「最佳化子查詢、衍生表格、檢視參考和通用表格運算式」。
影響半聯結策略的提示語法
hint_name([@query_block_name] [strategy [, strategy] ...])
此語法參照下列術語
hint_name
:允許使用這些提示名稱SEMIJOIN
、NO_SEMIJOIN
:啟用或停用已命名的半聯結策略。
strategy
:要啟用或停用的半聯結策略。允許使用這些策略名稱:DUPSWEEDOUT
、FIRSTMATCH
、LOOSESCAN
、MATERIALIZATION
。對於
SEMIJOIN
提示,如果未命名任何策略,則會根據optimizer_switch
系統變數啟用的策略,在可能的情況下使用半聯結。如果已命名策略,但不適用於該陳述式,則會使用DUPSWEEDOUT
。對於
NO_SEMIJOIN
提示,如果未命名任何策略,則不使用半聯結。如果命名的策略排除該陳述式的所有適用策略,則會使用DUPSWEEDOUT
。
如果一個子查詢巢狀在另一個子查詢內,且兩者都合併到外部查詢的半聯結中,則會忽略對最內層查詢的半聯結策略的任何指定。SEMIJOIN
和 NO_SEMIJOIN
提示仍可用於啟用或停用此類巢狀子查詢的半聯結轉換。
如果停用了 DUPSWEEDOUT
,則有時最佳化工具可能會產生遠非最佳的查詢計畫。發生這種情況的原因是在貪婪搜尋期間進行的啟發式修剪,可以透過設定 optimizer_prune_level=0
來避免。
範例
SELECT /*+ NO_SEMIJOIN(@subq1 FIRSTMATCH, LOOSESCAN) */ * FROM t2
WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);
SELECT /*+ SEMIJOIN(@subq1 MATERIALIZATION, DUPSWEEDOUT) */ * FROM t2
WHERE t2.a IN (SELECT /*+ QB_NAME(subq1) */ a FROM t3);
影響是否使用子查詢具體化或 IN
至 EXISTS
轉換的提示語法
SUBQUERY([@query_block_name] strategy)
提示名稱始終為 SUBQUERY
。
對於 SUBQUERY
提示,允許使用這些 strategy
值:INTOEXISTS
、MATERIALIZATION
。
範例
SELECT id, a IN (SELECT /*+ SUBQUERY(MATERIALIZATION) */ a FROM t1) FROM t2;
SELECT * FROM t2 WHERE t2.a IN (SELECT /*+ SUBQUERY(INTOEXISTS) */ a FROM t1);
對於半聯結(semijoin)和 SUBQUERY
提示,開頭的 @
指定提示所套用的查詢區塊。如果提示不包含開頭的 query_block_name
@
,則提示會套用至它所在的查詢區塊。若要為查詢區塊指定名稱,請參閱查詢區塊命名最佳化器提示。query_block_name
如果提示註解包含多個子查詢提示,則會使用第一個。如果有其他同類型的後續提示,則會產生警告。其他類型的後續提示會被靜默忽略。
MAX_EXECUTION_TIME
提示僅允許用於 SELECT
陳述式。它會限制陳述式執行時間上限 N
(以毫秒為單位的逾時值),超過此時間上限,伺服器會終止陳述式。
MAX_EXECUTION_TIME(N)
以下範例設定逾時時間為 1 秒(1000 毫秒)
SELECT /*+ MAX_EXECUTION_TIME(1000) */ * FROM t1 INNER JOIN t2 WHERE ...
MAX_EXECUTION_TIME(
提示會設定陳述式執行逾時時間為 N
)N
毫秒。如果此選項不存在或 N
為 0,則會套用由 max_execution_time
系統變數所建立的陳述式逾時時間。
MAX_EXECUTION_TIME
提示的適用情況如下:
對於具有多個
SELECT
關鍵字的陳述式,例如聯集或具有子查詢的陳述式,MAX_EXECUTION_TIME
會套用至整個陳述式,且必須出現在第一個SELECT
之後。它會套用至唯讀的
SELECT
陳述式。非唯讀的陳述式是指那些呼叫會以副作用修改資料的預存函式的陳述式。它不適用於預存程式中的
SELECT
陳述式,且會被忽略。
SET_VAR
提示會暫時設定系統變數的工作階段值(在單一陳述式的持續時間內)。範例:
SELECT /*+ SET_VAR(sort_buffer_size = 16M) */ name FROM people ORDER BY name;
INSERT /*+ SET_VAR(foreign_key_checks=OFF) */ INTO t2 VALUES(2);
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off') */ 1;
SET_VAR
提示的語法:
SET_VAR(var_name = value)
var_name
會命名具有工作階段值的系統變數(儘管並非所有此類變數都可以命名,如稍後說明)。value
是要指派給變數的值;該值必須是純量。
SET_VAR
會進行暫時性的變數變更,如下列陳述式所示:
mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 1 |
+-----------------+
mysql> SELECT /*+ SET_VAR(unique_checks=OFF) */ @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 0 |
+-----------------+
mysql> SELECT @@unique_checks;
+-----------------+
| @@unique_checks |
+-----------------+
| 1 |
+-----------------+
使用 SET_VAR
,不需要儲存和還原變數值。這可讓您以單一陳述式取代多個陳述式。請考慮以下陳述式序列:
SET @saved_val = @@SESSION.var_name;
SET @@SESSION.var_name = value;
SELECT ...
SET @@SESSION.var_name = @saved_val;
此序列可以由以下單一陳述式取代:
SELECT /*+ SET_VAR(var_name = value) ...
獨立的 SET
陳述式允許使用以下任何語法來命名工作階段變數:
SET SESSION var_name = value;
SET @@SESSION.var_name = value;
SET @@.var_name = value;
由於 SET_VAR
提示僅套用至工作階段變數,因此工作階段範圍是隱含的,而 SESSION
、@@SESSION.
和 @@
都不需要也不允許使用。包含明確的工作階段指標語法會導致 SET_VAR
提示被忽略並產生警告。
並非所有工作階段變數都允許與 SET_VAR
一起使用。個別系統變數的說明會指出每個變數是否可提示;請參閱第 7.1.8 節「伺服器系統變數」。您也可以在執行階段嘗試將系統變數與 SET_VAR
一起使用,來檢查該變數。如果變數不可提示,則會發生警告。
mysql> SELECT /*+ SET_VAR(collation_server = 'utf8mb4') */ 1;
+---+
| 1 |
+---+
| 1 |
+---+
1 row in set, 1 warning (0.00 sec)
mysql> SHOW WARNINGS\G
*************************** 1. row ***************************
Level: Warning
Code: 4537
Message: Variable 'collation_server' cannot be set using SET_VAR hint.
SET_VAR
語法僅允許設定單一變數,但可以提供多個提示來設定多個變數。
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=off')
SET_VAR(max_heap_table_size = 1G) */ 1;
如果同一個陳述式中出現多個具有相同變數名稱的提示,則會套用第一個提示,而其他提示會被忽略並產生警告。
SELECT /*+ SET_VAR(max_heap_table_size = 1G)
SET_VAR(max_heap_table_size = 3G) */ 1;
在此情況下,第二個提示會被忽略,並產生衝突的警告。
如果沒有系統變數具有指定的名稱或變數值不正確,則會忽略 SET_VAR
提示,並產生警告。
SELECT /*+ SET_VAR(max_size = 1G) */ 1;
SELECT /*+ SET_VAR(optimizer_switch = 'mrr_cost_based=yes') */ 1;
對於第一個陳述式,沒有 max_size
變數。對於第二個陳述式,mrr_cost_based
採用 on
或 off
的值,因此嘗試將其設定為 yes
是不正確的。在每種情況下,提示都會被忽略並產生警告。
SET_VAR
提示僅允許在陳述式層級使用。如果在子查詢中使用,則會忽略該提示並產生警告。
複本會忽略複寫陳述式中的 SET_VAR
提示,以避免潛在的安全性問題。
RESOURCE_GROUP
最佳化器提示用於資源群組管理(請參閱第 7.1.16 節「資源群組」)。此提示會將執行陳述式的執行緒暫時指派給已命名的資源群組(在陳述式的持續時間內)。它需要 RESOURCE_GROUP_ADMIN
或 RESOURCE_GROUP_USER
權限。
範例
SELECT /*+ RESOURCE_GROUP(USR_default) */ name FROM people ORDER BY name;
INSERT /*+ RESOURCE_GROUP(Batch) */ INTO t2 VALUES(2);
RESOURCE_GROUP
提示的語法:
RESOURCE_GROUP(group_name)
group_name
指示應將執行緒指派到的資源群組,以執行陳述式。如果該群組不存在,則會發生警告且該提示會被忽略。
RESOURCE_GROUP
提示必須出現在初始陳述式關鍵字(SELECT
、INSERT
、REPLACE
、UPDATE
或 DELETE
)之後。
RESOURCE_GROUP
的替代方案是 SET RESOURCE GROUP
陳述式,其會將執行緒非暫時性地指派給資源群組。請參閱第 15.7.2.4 節「SET RESOURCE GROUP 陳述式」。
資料表層級、索引層級和子查詢最佳化器提示允許將特定的查詢區塊命名為其引數語法的一部分。若要建立這些名稱,請使用 QB_NAME
提示,其會將名稱指派給提示所在的查詢區塊。
QB_NAME(name)
使用 QB_NAME
提示可以明確清楚地指出其他提示套用至哪個查詢區塊。它們也允許在單一提示註解中指定所有非查詢區塊名稱提示,以便更容易理解複雜的陳述式。請考慮以下陳述式:
SELECT ...
FROM (SELECT ...
FROM (SELECT ... FROM ...)) ...
QB_NAME
提示會將名稱指派給陳述式中的查詢區塊。
SELECT /*+ QB_NAME(qb1) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...
然後其他提示可以使用這些名稱來參照適當的查詢區塊。
SELECT /*+ QB_NAME(qb1) MRR(@qb1 t1) BKA(@qb2) NO_MRR(@qb3t1 idx1, id2) */ ...
FROM (SELECT /*+ QB_NAME(qb2) */ ...
FROM (SELECT /*+ QB_NAME(qb3) */ ... FROM ...)) ...
產生的效果如下:
MRR(@qb1 t1)
套用至查詢區塊qb1
中的資料表t1
。BKA(@qb2)
套用至查詢區塊qb2
。NO_MRR(@qb3 t1 idx1, id2)
套用至查詢區塊qb3
中資料表t1
的索引idx1
和idx2
。
查詢區塊名稱是識別碼,並遵循有關有效名稱以及如何將其加上引號的通常規則(請參閱第 11.2 節「結構描述物件名稱」)。例如,包含空格的查詢區塊名稱必須加上引號,這可以使用反引號來完成。
SELECT /*+ BKA(@`my hint name`) */ ...
FROM (SELECT /*+ QB_NAME(`my hint name`) */ ...) ...
如果啟用 ANSI_QUOTES
SQL 模式,則也可以在雙引號內將查詢區塊名稱加上引號。
SELECT /*+ BKA(@"my hint name") */ ...
FROM (SELECT /*+ QB_NAME("my hint name") */ ...) ...