文件首頁
MySQL 8.4 參考手冊
相關文件 下載本手冊
PDF (美式信紙) - 39.9Mb
PDF (A4) - 40.0Mb
Man Pages (TGZ) - 258.5Kb
Man Pages (Zip) - 365.5Kb
Info (Gzip) - 4.0Mb
Info (Zip) - 4.0Mb


MySQL 8.4 參考手冊  /  ...  /  優化器提示

10.9.3 優化器提示

控制優化器策略的一種方法是設定 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 可用的優化器提示

提示名稱 說明 適用範圍
BKANO_BKA 影響批次金鑰存取聯結處理 查詢區塊、資料表
BNLNO_BNL 影響雜湊聯結最佳化 查詢區塊、資料表
DERIVED_CONDITION_PUSHDOWNNO_DERIVED_CONDITION_PUSHDOWN 使用或忽略實體化衍生資料表的衍生條件下推最佳化 查詢區塊、資料表
GROUP_INDEXNO_GROUP_INDEX 使用或忽略在 GROUP BY 操作中用於索引掃描的指定索引或索引 索引
HASH_JOINNO_HASH_JOIN 影響雜湊聯結最佳化 (在 MySQL 8.4 中無效) 查詢區塊、資料表
INDEXNO_INDEX 作為 JOIN_INDEXGROUP_INDEXORDER_INDEX 的組合,或作為 NO_JOIN_INDEXNO_GROUP_INDEXNO_ORDER_INDEX 的組合。 索引
INDEX_MERGENO_INDEX_MERGE 影響索引合併最佳化 表格、索引
JOIN_FIXED_ORDER 在聯結順序中使用 FROM 子句中指定的表格順序 查詢區塊
JOIN_INDEXNO_JOIN_INDEX 針對任何存取方法,使用或忽略指定的索引或多個索引 索引
JOIN_ORDER 在聯結順序中使用提示中指定的表格順序 查詢區塊
JOIN_PREFIX 在聯結順序的開頭表格中使用提示中指定的表格順序 查詢區塊
JOIN_SUFFIX 在聯結順序的最後表格中使用提示中指定的表格順序 查詢區塊
MAX_EXECUTION_TIME 限制語句執行時間 全域
MERGENO_MERGE 影響衍生表格/檢視表合併到外部查詢區塊 表格
MRRNO_MRR 影響多範圍讀取最佳化 表格、索引
NO_ICP 影響索引條件下推最佳化 表格、索引
NO_RANGE_OPTIMIZATION 影響範圍最佳化 表格、索引
ORDER_INDEXNO_ORDER_INDEX 使用或忽略指定的索引或多個索引來排序資料列 索引
QB_NAME 為查詢區塊指派名稱 查詢區塊
RESOURCE_GROUP 在語句執行期間設定資源群組 全域
SEMIJOINNO_SEMIJOIN 影響半聯結和反聯結策略 查詢區塊
SKIP_SCANNO_SKIP_SCAN 影響跳躍掃描最佳化 表格、索引
SET_VAR 在語句執行期間設定變數 全域
SUBQUERY 影響具體化、INEXISTS 子查詢策略 查詢區塊

停用最佳化會防止最佳化工具使用它。啟用最佳化表示如果該策略適用於語句執行,則最佳化工具可以自由使用該策略,而不是表示最佳化工具一定會使用它。

最佳化工具提示語法

MySQL 支援 SQL 語句中的註解,如 第 11.7 節「註解」中所述。最佳化工具提示必須在 /*+ ... */ 註解中指定。也就是說,最佳化工具提示使用 /* ... */ C 樣式註解語法的變體,在 /* 註解開頭序列之後加上 + 字元。範例:

/*+ BKA(t1) */
/*+ BNL(t1, t2) */
/*+ NO_RANGE_OPTIMIZATION(t4 PRIMARY) */
/*+ QB_NAME(qb2) */

+ 字元後允許使用空格。

剖析器會在 SELECTUPDATEINSERTREPLACEDELETE 語句的初始關鍵字之後識別最佳化工具提示註解。在這些內容中允許使用提示:

  • 在查詢和資料變更語句的開頭

    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);

提示會控制合併到外部查詢區塊的半聯結表格的行為。如果子查詢 subq1subq2 轉換為半聯結,則表格 t4@subq1t5@subq2 會合併到外部查詢區塊。在這種情況下,在外部查詢區塊中指定的提示會控制表格 t4@subq1t5@subq2 的行為。

最佳化工具會根據下列原則解析聯結順序提示:

  • 多個提示執行個體

    只會套用每個類型的一個 JOIN_PREFIXJOIN_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_ORDERJOIN_PREFIX 的表格順序無法同時套用時:

    SELECT /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */ ... FROM t1, t2;

    在這種情況下,會套用第一個指定的提示,並且會忽略後續衝突的提示,而不會顯示警告。無法套用的有效提示會被靜默忽略,而不會顯示警告。

  • 忽略的提示

    如果提示中指定的表格具有循環相依性,則會忽略提示。

    範例:

    /*+ JOIN_ORDER(t1, t2) JOIN_PREFIX(t2, t1) */

    JOIN_ORDER 提示會設定表格 t2 相依於 t1JOIN_PREFIX 提示會被忽略,因為表格 t1 無法相依於 t2。忽略的提示不會顯示在擴充的 EXPLAIN 輸出中。

  • const 表格的互動

    MySQL 最佳化工具會將 const 表格放置在聯結順序的第一個位置,並且 const 表格的位置不會受到提示的影響。在聯結順序提示中參照 const 表格會被忽略,但提示仍然適用。例如,這些是等效的:

    JOIN_ORDER(t1, const_tbl, t2)
    JOIN_ORDER(t1, t2)

    在擴充的 EXPLAIN 輸出中顯示的接受提示包括指定的 const 表格。

  • 與聯結作業類型的互動

    MySQL 支援數種聯結類型:LEFTRIGHTINNERCROSSSTRAIGHT_JOIN。若提示與指定的聯結類型衝突,則會忽略該提示,且不會發出警告。

    範例:

    SELECT /*+ JOIN_PREFIX(t1, t2) */FROM t2 LEFT JOIN t1;

    此處,提示中要求的聯結順序與 LEFT JOIN 所要求的順序之間發生衝突。該提示會被忽略,且不會發出警告。

資料表層級最佳化提示

資料表層級提示會影響:

這些提示類型適用於特定資料表,或查詢區塊中的所有資料表。

資料表層級提示的語法

hint_name([@query_block_name] [tbl_name [, tbl_name] ...])
hint_name([tbl_name@query_block_name [, tbl_name@query_block_name] ...])

語法會參照下列術語:

  • hint_name:允許使用下列提示名稱:

    注意

    若要使用區塊巢狀迴圈或批次金鑰存取提示來為外部聯結的任何內部資料表啟用聯結緩衝,則必須為外部聯結的所有內部資料表啟用聯結緩衝。

  • 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 是傳送者資料表。

對於 MERGENO_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_INDEXNO_GROUP_INDEX:啟用或停用指定索引或索引的 GROUP BY 作業索引掃描。相當於索引提示 FORCE INDEX FOR GROUP BYIGNORE INDEX FOR GROUP BY

    • INDEXNO_INDEX:作用類似於 JOIN_INDEXGROUP_INDEXORDER_INDEX 的組合,強制伺服器在任何和所有範圍內使用指定的索引或索引,或類似於 NO_JOIN_INDEXNO_GROUP_INDEXNO_ORDER_INDEX 的組合,這會導致伺服器在任何和所有範圍內忽略指定的索引或索引。相當於 FORCE INDEXIGNORE INDEX

    • INDEX_MERGENO_INDEX_MERGE:啟用或停用指定資料表或索引的索引合併存取方法。有關此存取方法的資訊,請參閱第 10.2.1.3 節「索引合併最佳化」。這些提示適用於所有三個索引合併演算法。

      INDEX_MERGE 提示強制最佳化工具使用索引合併,並針對指定的資料表使用指定的索引集。如果未指定索引,則最佳化工具會考慮所有可能的索引組合,並選擇最不昂貴的組合。如果索引組合不適用於給定的語句,則可能會忽略該提示。

      NO_INDEX_MERGE 提示會停用涉及任何指定索引的索引合併組合。如果提示未指定索引,則不允許該資料表進行索引合併。

    • JOIN_INDEXNO_JOIN_INDEX:強制 MySQL 使用或忽略指定的索引或索引,以用於任何存取方法,例如 refrangeindex_merge 等。相當於 FORCE INDEX FOR JOINIGNORE INDEX FOR JOIN

    • MRRNO_MRR:啟用或停用指定資料表或索引的 MRR。MRR 提示僅適用於 InnoDBMyISAM 資料表。有關此存取方法的資訊,請參閱第 10.2.1.11 節「多範圍讀取最佳化」

    • NO_ICP:停用指定資料表或索引的 ICP。預設情況下,ICP 是一種候選最佳化策略,因此沒有用於啟用的提示。有關此存取方法的資訊,請參閱第 10.2.1.6 節「索引條件下推最佳化」

    • NO_RANGE_OPTIMIZATION:停用指定資料表或索引的索引範圍存取。此提示也會停用該資料表或索引的索引合併和寬鬆索引掃描。預設情況下,範圍存取是一種候選最佳化策略,因此沒有用於啟用的提示。

      當範圍數量可能很高且範圍最佳化需要大量資源時,此提示可能很有用。

    • ORDER_INDEXNO_ORDER_INDEX:使 MySQL 使用或忽略指定的索引或索引來對列進行排序。相當於 FORCE INDEX FOR ORDER BYIGNORE INDEX FOR ORDER BY

    • SKIP_SCANNO_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 具有欄位 abcd;且在 abc 上分別存在名為 i_ai_bi_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_MERGENO_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_INDEXINDEXJOIN_INDEXORDER_INDEX 的優先順序都高於對等的 FORCE INDEX 提示;也就是說,它們會導致忽略 FORCE INDEX 提示。同樣地,NO_GROUP_INDEXNO_INDEXNO_JOIN_INDEXNO_ORDER_INDEX 提示的優先順序都高於任何 IGNORE INDEX 對等項目,也會導致它們被忽略。

    索引層級最佳化工具提示 GROUP_INDEXNO_GROUP_INDEXINDEXNO_INDEXJOIN_INDEXNO_JOIN_INDEXORDER_INDEXNO_ORDER_INDEX 提示的優先順序都高於所有其他最佳化工具提示,包括其他索引層級最佳化工具提示。任何其他最佳化工具提示僅會套用至這些提示所允許的索引。

    GROUP_INDEXINDEXJOIN_INDEXORDER_INDEX 提示都相當於 FORCE INDEX,而不是 USE INDEX。這是因為使用一或多個這些提示表示,只有在無法使用任何指定的索引在表格中尋找資料列時,才會使用表格掃描。若要讓 MySQL 使用與指定 USE INDEX 執行個體相同的索引或索引集,您可以使用 NO_INDEXNO_JOIN_INDEXNO_GROUP_INDEXNO_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_INDEXUSE 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 INDEXFORCE INDEXIGNORE INDEX 索引提示的優先順序高於 INDEX_MERGENO_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_ai_b 來進行 rangeref 存取。沒有任何衝突;兩個提示都適用。

  • 如果 IGNORE INDEX 提示指定多個索引,則這些索引無法用於索引合併。

  • FORCE INDEXUSE 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:允許使用下列提示名稱:

  • strategy:要啟用或停用的半聯結策略。允許使用這些策略名稱:DUPSWEEDOUTFIRSTMATCHLOOSESCANMATERIALIZATION

    對於 SEMIJOIN 提示,如果未命名任何策略,則會根據 optimizer_switch 系統變數啟用的策略,盡可能使用半聯結。如果命名的策略不適用於該陳述式,則會使用 DUPSWEEDOUT

    對於 NO_SEMIJOIN 提示,如果未命名任何策略,則不會使用半聯結。如果命名的策略排除該陳述式的所有適用策略,則會使用 DUPSWEEDOUT

如果一個子查詢巢狀於另一個子查詢中,且兩者都合併至外部查詢的半聯結中,則會忽略最內層查詢的任何半聯結策略指定。 SEMIJOINNO_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 值:INTOEXISTSMATERIALIZATION

範例

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 的值為 onoff,因此嘗試將其設定為 yes 是不正確的。在每種情況下,都會忽略提示並顯示警告。

SET_VAR 提示僅允許在語句層級使用。如果在子查詢中使用,則會忽略提示並顯示警告。

複本會忽略複寫語句中的 SET_VAR 提示,以避免潛在的安全性問題。

資源群組提示語法

RESOURCE_GROUP 最佳化器提示用於資源群組管理(請參閱第 7.1.16 節「資源群組」)。此提示會將執行語句的執行緒暫時(僅限於語句的持續時間)指派給指定的資源群組。它需要 RESOURCE_GROUP_ADMINRESOURCE_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 提示必須出現在初始語句關鍵字(SELECTINSERTREPLACEUPDATEDELETE)之後。

除了 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 ...)) ...

產生的效果如下

查詢區塊名稱是識別碼,並遵循關於哪些名稱有效以及如何引用它們的常用規則(請參閱第 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") */ ...) ...