控制優化器策略的一種方法是設定 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 8.4 中無效) | 查詢區塊、資料表 |
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
所要求的順序之間發生衝突。該提示會被忽略,且不會發出警告。
資料表層級提示會影響:
區塊巢狀迴圈 (Block Nested-Loop, BNL) 和批次金鑰存取 (Batched Key Access, 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 8.4 中沒有作用;請改用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
,則會將區塊巢狀迴圈聯結套用到 t2
,方法是在開始從 t2
讀取之前緩衝來自 t1
的列。如果最佳化工具改為選擇先處理 t2
,則提示無效,因為 t2
是傳送者資料表。
對於 MERGE
和 NO_MERGE
提示,適用以下優先順序規則:
提示的優先順序高於任何非技術約束的最佳化工具啟發式方法。(如果將提示作為建議沒有效果,則最佳化工具有忽略它的理由。)
提示的優先順序高於
derived_merge
旗標的optimizer_switch
系統變數。對於視圖參考,視圖定義中的
ALGORITHM={MERGE|TEMPTABLE}
子句的優先順序高於在參考視圖的查詢中指定的提示。
索引層級提示會影響最佳化工具用於特定資料表或索引的索引處理策略。這些提示類型會影響索引條件下推 (Index Condition Pushdown, ICP)、多範圍讀取 (Multi-Range Read, 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 存取方法。如需此存取方法的相關資訊,請參閱Skip Scan 範圍存取方法。SKIP_SCAN
提示會強制最佳化工具使用 Skip Scan 來處理指定的表格,並使用指定的索引集。如果沒有指定索引,最佳化工具會考慮所有可能的索引,並選擇成本最低的索引。如果索引不適用於給定的陳述式,則可能會忽略此提示。NO_SKIP_SCAN
提示會針對指定的索引停用 Skip Scan。如果此提示沒有指定任何索引,則該表格不允許使用 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
使用索引合併,但會強制最佳化工具使用i_a
或i_b
來進行range
或ref
存取。沒有任何衝突;兩個提示都適用。如果
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") */ ...) ...