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


MySQL 9.0 參考手冊  /  ...  /  可切換的優化

10.9.2 可切換的優化

optimizer_switch 系統變數可控制最佳化工具的行為。其值是一組旗標,每個旗標的值為 onoff,表示是否啟用或停用對應的最佳化工具行為。此變數具有全域和工作階段值,並且可以在執行期間變更。全域預設值可以在伺服器啟動時設定。

若要查看目前設定的最佳化工具旗標,請選取變數值

mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,
                    index_merge_sort_union=on,index_merge_intersection=on,
                    engine_condition_pushdown=on,index_condition_pushdown=on,
                    mrr=on,mrr_cost_based=on,block_nested_loop=on,
                    batched_key_access=off,materialization=on,semijoin=on,
                    loosescan=on,firstmatch=on,duplicateweedout=on,
                    subquery_materialization_cost_based=on,
                    use_index_extensions=on,condition_fanout_filter=on,
                    derived_merge=on,use_invisible_indexes=off,skip_scan=on,
                    hash_join=on,subquery_to_derived=off,
                    prefer_ordering_index=on,hypergraph_optimizer=off,
                    derived_condition_pushdown=on,hash_set_operations=on
1 row in set (0.00 sec)

若要變更 optimizer_switch 的值,請指派一個值,其中包含一個或多個命令的逗號分隔清單

SET [GLOBAL|SESSION] optimizer_switch='command[,command]...';

每個 command 值應具有下表中顯示的其中一種格式。

命令語法 意義
default 將每個最佳化重設為其預設值
opt_name=default 將指定的最佳化設定為其預設值
opt_name=off 停用指定的最佳化
opt_name=on 啟用指定的最佳化

值中命令的順序無關緊要,但如果存在 default 命令,則會先執行。將 opt_name 旗標設定為 default 會將其設定為其預設值 onoff。不允許在值中多次指定任何指定的 opt_name,並且會導致錯誤。值中的任何錯誤都會導致指派失敗並出現錯誤,而 optimizer_switch 的值保持不變。

以下清單說明允許的 opt_name 旗標名稱,按最佳化策略分組

  • 批次索引鍵存取旗標

    batched_key_access 設定為 on 時,若要使其生效,mrr 旗標也必須為 on。目前,MRR 的成本估計過於悲觀。因此,也必須將 mrr_cost_based 設定為 off 才能使用 BKA。

    更多資訊,請參閱第 10.2.1.12 節「區塊巢狀迴圈和批次金鑰存取聯結」

  • 區塊巢狀迴圈旗標

    更多資訊,請參閱第 10.2.1.12 節「區塊巢狀迴圈和批次金鑰存取聯結」

  • 條件篩選旗標

    更多資訊,請參閱第 10.2.1.13 節「條件篩選」

  • 衍生條件下推旗標

    更多資訊,請參閱第 10.2.2.5 節「衍生條件下推最佳化」

  • 衍生資料表合併旗標

    • derived_merge (預設 on)

      控制將衍生資料表和檢視合併到外部查詢區塊。

    derived_merge 旗標控制最佳化工具是否嘗試將衍生資料表、檢視參照和通用資料表運算式合併到外部查詢區塊中,假設沒有其他規則阻止合併;例如,檢視的 ALGORITHM 指令會優先於 derived_merge 設定。依預設,此旗標為 on 以啟用合併。

    更多資訊,請參閱第 10.2.2.4 節「使用合併或實體化最佳化衍生資料表、檢視參照和通用資料表運算式」

  • 引擎條件下推旗標

    更多資訊,請參閱第 10.2.1.5 節「引擎條件下推最佳化」

  • 雜湊聯結旗標

    更多資訊,請參閱第 10.2.1.4 節「雜湊聯結最佳化」

  • 索引條件下推旗標

    更多資訊,請參閱第 10.2.1.6 節「索引條件下推最佳化」

  • 索引擴充旗標

    更多資訊,請參閱第 10.3.10 節「索引擴充的使用」

  • 索引合併旗標

    更多資訊,請參閱第 10.2.1.3 節「索引合併最佳化」

  • 索引可見性旗標

    更多資訊,請參閱第 10.3.12 節「不可見索引」

  • 限制最佳化旗標

    • prefer_ordering_index (預設 on)

      控制在具有 ORDER BYGROUP BYLIMIT 子句的查詢中,最佳化工具是否嘗試使用排序索引而非未排序索引、檔案排序或其他最佳化。當最佳化工具判斷使用此最佳化可更快執行查詢時,預設會執行此最佳化。

      由於進行此判斷的演算法無法處理所有可能的情況(部分原因是假設資料的分佈總是或多或少是均勻的),因此在某些情況下,此最佳化可能不是理想的。可以將 prefer_ordering_index 旗標設定為 off 來停用此最佳化。

    更多資訊和範例,請參閱第 10.2.1.19 節「LIMIT 查詢最佳化」

  • 多範圍讀取旗標

    • mrr (預設 on)

      控制多範圍讀取策略。

    • mrr_cost_based (預設 on)

      如果 mrr=on,則控制使用基於成本的 MRR。

    更多資訊,請參閱第 10.2.1.11 節「多範圍讀取最佳化」

  • 半聯結旗標

    • duplicateweedout (預設 on)

      控制半聯結重複資料消除策略。

    • firstmatch (預設 on)

      控制半聯結 FirstMatch 策略。

    • loosescan (預設 on)

      控制半聯結 LooseScan 策略(不要與 GROUP BY 的寬鬆索引掃描混淆)。

    • semijoin (預設 on)

      控制所有半聯結策略。

      這也適用於反聯結最佳化。

    semijoinfirstmatchloosescanduplicateweedout 旗標可啟用對半聯結策略的控制。semijoin 旗標控制是否使用半聯結。如果設定為 on,則 firstmatchloosescan 旗標可更精細地控制允許的半聯結策略。

    如果停用 duplicateweedout 半聯結策略,則除非也停用所有其他適用的策略,否則不會使用此策略。

    如果 semijoinmaterialization 都為 on,則半聯結也會在適用的情況下使用實體化。這些旗標預設為 on

    更多資訊,請參閱使用半聯結轉換最佳化 IN 和 EXISTS 子查詢述詞

  • 集合運算旗標

    • hash_set_operations (預設 on)

      啟用涉及 EXCEPTINTERSECT) 的集合運算的雜湊表最佳化;預設啟用。否則,會使用基於暫存資料表的重複資料刪除,如同先前版本的 MySQL。

      此最佳化用於雜湊的記憶體量可以使用 set_operations_buffer_size 系統變數來控制;增加此值通常會加快使用這些運算的語句的執行時間。

  • 跳過掃描旗標

    • skip_scan (預設 on)

      控制跳過掃描存取方法的使用。

    更多資訊,請參閱跳過掃描範圍存取方法

  • 子查詢實體化旗標

    materialization 旗標控制是否使用子查詢實體化。如果 semijoinmaterialization 都為 on,則半聯結也會在適用的情況下使用實體化。這些旗標預設為 on

    subquery_materialization_cost_based 旗標可控制子查詢具體化和 IN 轉換為 EXISTS 子查詢之間的選擇。如果旗標為 on (預設值),最佳化工具會在子查詢具體化和 IN 轉換為 EXISTS 子查詢之間,根據成本來選擇適用的方法。如果旗標為 off,最佳化工具會選擇子查詢具體化,而不是 IN 轉換為 EXISTS 子查詢。

    如需更多資訊,請參閱 第 10.2.2 節「最佳化子查詢、衍生表、檢視參照和通用表表示式」

  • 子查詢轉換旗標

    • subquery_to_derived (預設值 off)

      在許多情況下,最佳化工具能夠將 SELECTWHEREJOINHAVING 子句中的純量子查詢轉換為衍生表的左外部聯結。(根據衍生表是否可為 NULL,有時可以進一步簡化為內部聯結。) 此轉換適用於滿足下列條件的子查詢:

      • 子查詢不使用任何非決定性函數,例如 RAND()

      • 子查詢不是可以使用 MIN()MAX() 重寫的 ANYALL 子查詢。

      • 父查詢未設定使用者變數,因為重寫可能會影響執行順序,如果同一個查詢中多次存取該變數,可能會導致非預期的結果。

      • 子查詢不應是相關的,也就是說,它不應參照外部查詢中表格的欄位,或包含在外部查詢中評估的彙總。

      此最佳化也適用於 INNOT INEXISTSNOT EXISTS 的表格子查詢引數,且該引數不包含 GROUP BY

      此旗標的預設值為 off,因為在大多數情況下,啟用此最佳化並不會產生任何明顯的效能改進 (而且在許多情況下甚至會使查詢執行速度變慢),但您可以透過將 subquery_to_derived 旗標設定為 on 來啟用此最佳化。它主要用於測試。

      範例,使用純量子查詢

      d
      mysql> CREATE TABLE t1(a INT);
      
      mysql> CREATE TABLE t2(a INT);
      
      mysql> INSERT INTO t1 VALUES ROW(1), ROW(2), ROW(3), ROW(4);
      
      mysql> INSERT INTO t2 VALUES ROW(1), ROW(2);
      
      mysql> SELECT * FROM t1
          ->     WHERE t1.a > (SELECT COUNT(a) FROM t2);
      +------+
      | a    |
      +------+
      |    3 |
      |    4 |
      +------+
      
      mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=off%';
      +-----------------------------------------------------+
      | @@optimizer_switch LIKE '%subquery_to_derived=off%' |
      +-----------------------------------------------------+
      |                                                   1 |
      +-----------------------------------------------------+
      
      mysql> EXPLAIN SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 4
           filtered: 33.33
              Extra: Using where
      *************************** 2. row ***************************
                 id: 2
        select_type: SUBQUERY
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 2
           filtered: 100.00
              Extra: NULL
      
      mysql> SET @@optimizer_switch='subquery_to_derived=on';
      
      
      mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=off%';
      +-----------------------------------------------------+
      | @@optimizer_switch LIKE '%subquery_to_derived=off%' |
      +-----------------------------------------------------+
      |                                                   0 |
      +-----------------------------------------------------+
      
      mysql> SELECT @@optimizer_switch LIKE '%subquery_to_derived=on%';
      +----------------------------------------------------+
      | @@optimizer_switch LIKE '%subquery_to_derived=on%' |
      +----------------------------------------------------+
      |                                                  1 |
      +----------------------------------------------------+
      
      mysql> EXPLAIN SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: <derived2>
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 1
           filtered: 100.00
              Extra: NULL
      *************************** 2. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 4
           filtered: 33.33
              Extra: Using where; Using join buffer (hash join)
      *************************** 3. row ***************************
                 id: 2
        select_type: DERIVED
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 2
           filtered: 100.00
              Extra: NULL

      從執行第二個 EXPLAIN 陳述式後立即執行的 SHOW WARNINGS 中可以看出,在啟用最佳化的情況下,查詢 SELECT * FROM t1 WHERE t1.a > (SELECT COUNT(a) FROM t2) 會以類似於此處顯示的形式重寫

      SELECT t1.a FROM t1
          JOIN  ( SELECT COUNT(t2.a) AS c FROM t2 ) AS d
                  WHERE t1.a > d.c;

      範例,使用包含 IN (子查詢) 的查詢

      mysql> DROP TABLE IF EXISTS t1, t2;
      
      mysql> CREATE TABLE t1 (a INT, b INT);
      mysql> CREATE TABLE t2 (a INT, b INT);
      
      mysql> INSERT INTO t1 VALUES ROW(1,10), ROW(2,20), ROW(3,30);
      mysql> INSERT INTO t2
          ->    VALUES ROW(1,10), ROW(2,20), ROW(3,30), ROW(1,110), ROW(2,120), ROW(3,130);
      
      mysql> SELECT * FROM t1
          ->     WHERE   t1.b < 0
          ->             OR
          ->             t1.a IN (SELECT t2.a + 1 FROM t2);
      +------+------+
      | a    | b    |
      +------+------+
      |    2 |   20 |
      |    3 |   30 |
      +------+------+
      
      mysql> SET @@optimizer_switch="subquery_to_derived=off";
      
      mysql> EXPLAIN SELECT * FROM t1
          ->             WHERE   t1.b < 0
          ->                     OR
          ->                     t1.a IN (SELECT t2.a + 1 FROM t2)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 3
           filtered: 100.00
              Extra: Using where
      *************************** 2. row ***************************
                 id: 2
        select_type: DEPENDENT SUBQUERY
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 6
           filtered: 100.00
              Extra: Using where
      
      mysql> SET @@optimizer_switch="subquery_to_derived=on";
      
      mysql> EXPLAIN SELECT * FROM t1
          ->             WHERE   t1.b < 0
          ->                     OR
          ->                     t1.a IN (SELECT t2.a + 1 FROM t2)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 3
           filtered: 100.00
              Extra: NULL
      *************************** 2. row ***************************
                 id: 1
        select_type: PRIMARY
              table: <derived2>
         partitions: NULL
               type: ref
      possible_keys: <auto_key0>
                key: <auto_key0>
            key_len: 9
                ref: std2.t1.a
               rows: 2
           filtered: 100.00
              Extra: Using where; Using index
      *************************** 3. row ***************************
                 id: 2
        select_type: DERIVED
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 6
           filtered: 100.00
              Extra: Using temporary

      在對此查詢執行 EXPLAIN 後,檢查並簡化 SHOW WARNINGS 的結果,結果顯示,當啟用 subquery_to_derived 旗標時,SELECT * FROM t1 WHERE t1.b < 0 OR t1.a IN (SELECT t2.a + 1 FROM t2) 會以類似於此處顯示的形式重寫

      SELECT a, b FROM t1
          LEFT JOIN (SELECT DISTINCT a + 1 AS e FROM t2) d
          ON t1.a = d.e
          WHERE   t1.b < 0
                  OR
                  d.e IS NOT NULL;

      範例,使用包含 EXISTS (子查詢) 的查詢,並使用與前一個範例相同的表格和資料

      mysql> SELECT * FROM t1
          ->     WHERE   t1.b < 0
          ->             OR
          ->             EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1);
      +------+------+
      | a    | b    |
      +------+------+
      |    1 |   10 |
      |    2 |   20 |
      +------+------+
      
      mysql> SET @@optimizer_switch="subquery_to_derived=off";
      
      mysql> EXPLAIN SELECT * FROM t1
          ->             WHERE   t1.b < 0
          ->                     OR
          ->                     EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 3
           filtered: 100.00
              Extra: Using where
      *************************** 2. row ***************************
                 id: 2
        select_type: DEPENDENT SUBQUERY
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 6
           filtered: 16.67
              Extra: Using where
      
      mysql> SET @@optimizer_switch="subquery_to_derived=on";
      
      mysql> EXPLAIN SELECT * FROM t1
          ->             WHERE   t1.b < 0
          ->                     OR
          ->                     EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)\G
      *************************** 1. row ***************************
                 id: 1
        select_type: PRIMARY
              table: t1
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 3
           filtered: 100.00
              Extra: NULL
      *************************** 2. row ***************************
                 id: 1
        select_type: PRIMARY
              table: <derived2>
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 6
           filtered: 100.00
              Extra: Using where; Using join buffer (hash join)
      *************************** 3. row ***************************
                 id: 2
        select_type: DERIVED
              table: t2
         partitions: NULL
               type: ALL
      possible_keys: NULL
                key: NULL
            key_len: NULL
                ref: NULL
               rows: 6
           filtered: 100.00
              Extra: Using temporary

      如果我們在對查詢 SELECT * FROM t1 WHERE t1.b < 0 OR EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1) 執行 EXPLAIN 後執行 SHOW WARNINGS,且 subquery_to_derived 已啟用,並且簡化結果的第二列,我們會看到它已重寫為類似於此的形式

      SELECT a, b FROM t1
      LEFT JOIN (SELECT DISTINCT 1 AS e1, t2.a AS e2 FROM t2) d
      ON t1.a + 1 = d.e2
      WHERE   t1.b < 0
              OR
              d.e1 IS NOT NULL;

      如需更多資訊,請參閱 第 10.2.2.4 節「透過合併或具體化來最佳化衍生表、檢視參照和通用表表示式」,以及 第 10.2.1.19 節「LIMIT 查詢最佳化」使用半聯結轉換來最佳化 IN 和 EXISTS 子查詢述詞

當您將值指派給 optimizer_switch 時,未提及的旗標會保留其目前的值。這使得可以在單一陳述式中啟用或停用特定的最佳化工具行為,而不會影響其他行為。該陳述式不依賴於其他最佳化工具旗標的存在及其值。假設所有索引合併最佳化都已啟用

mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=on,
                    index_merge_sort_union=on,index_merge_intersection=on,
                    engine_condition_pushdown=on,index_condition_pushdown=on,
                    mrr=on,mrr_cost_based=on,block_nested_loop=on,
                    batched_key_access=off,materialization=on,semijoin=on,
                    loosescan=on, firstmatch=on,
                    subquery_materialization_cost_based=on,
                    use_index_extensions=on,condition_fanout_filter=on,
                    derived_merge=on,use_invisible_indexes=off,skip_scan=on,
                    hash_join=on,subquery_to_derived=off,
                    prefer_ordering_index=on

如果伺服器針對某些查詢使用索引合併聯集或索引合併排序聯集存取方法,而您想要檢查最佳化工具是否可以在沒有這些方法的情況下執行得更好,請將變數值設定如下:

mysql> SET optimizer_switch='index_merge_union=off,index_merge_sort_union=off';

mysql> SELECT @@optimizer_switch\G
*************************** 1. row ***************************
@@optimizer_switch: index_merge=on,index_merge_union=off,
                    index_merge_sort_union=off,index_merge_intersection=on,
                    engine_condition_pushdown=on,index_condition_pushdown=on,
                    mrr=on,mrr_cost_based=on,block_nested_loop=on,
                    batched_key_access=off,materialization=on,semijoin=on,
                    loosescan=on, firstmatch=on,
                    subquery_materialization_cost_based=on,
                    use_index_extensions=on,condition_fanout_filter=on,
                    derived_merge=on,use_invisible_indexes=off,skip_scan=on,
                    hash_join=on,subquery_to_derived=off,
                    prefer_ordering_index=on