optimizer_switch
システム変数を使用するとオプティマイザの動作を制御できます。 その値はフラグのセットで、それぞれ対応するオプティマイザの動作を有効にするかまたは無効にするかを示す on
または off
の値を持ちます。 この変数はグローバル値およびセッション値を持ち、実行時に変更できます。 グローバル値のデフォルトはサーバーの起動時に設定できます。
オプティマイザの現在のフラグセットを表示するには、変数値を選択します。
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
1 row in set (0.00 sec)
optimizer_switch
の値を変更するには、1 つ以上のコマンドのカンマ区切りのリストから構成される値を割り当てます。
SET [GLOBAL|SESSION] optimizer_switch='command[,command]...';
各 command
値は、次の表に示すいずれかの形式になるようにしてください。
コマンドの構文 | 意味 |
---|---|
default |
すべての最適化をそのデフォルト値にリセットします |
|
指定した最適化をそのデフォルト値に設定します |
|
指定した最適化を無効にします |
|
指定した最適化を有効にします |
default
コマンドが存在する場合最初に実行されますが、値の中のコマンドの順序は問題ではありません。 opt_name
フラグを default
に設定すると、そのデフォルト値が on
または off
のどちらであってもそれに設定されます。 値に特定の opt_name
を複数回指定することは許可されず、エラーが発生します。 値のエラーによって、割り当てがエラーを伴って失敗し、optimizer_switch
の値が変更されないままになります。
次のリストは、最適化戦略別にグループ化された、許可される opt_name
フラグ名を示しています:
-
バッチキーアクセスフラグ
batched_key_access
がon
に設定されている場合に何らかの効果を持つためには、mrr
フラグもon
である必要があります。 現在、MRR のコスト見積もりはきわめて悲観的です。 したがって、BKA を使用するには、mrr_cost_based
をoff
にする必要もあります。詳細は、セクション8.2.1.12「Block Nested Loop 結合と Batched Key Access 結合」を参照してください。
-
ブロックネストループフラグ
詳細は、セクション8.2.1.12「Block Nested Loop 結合と Batched Key Access 結合」を参照してください。
-
条件フィルタリングフラグ
詳細は、セクション8.2.1.13「条件フィルタ」を参照してください。
-
導出条件プッシュダウンフラグ
詳細は、セクション8.2.2.5「導出条件プッシュダウン最適化」を参照してください
-
導出テーブルマージフラグ
derived_merge
フラグは、オプティマイザが導出テーブル、ビュー参照および共通テーブル式を外部クエリーブロックにマージしようとするかどうかを制御します。ただし、他のルールがマージを妨げることはないと想定しています。たとえば、ビューのALGORITHM
ディレクティブがderived_merge
設定より優先されます。 デフォルトでは、マージを有効にするフラグはon
です。詳細は、セクション8.2.2.4「マージまたは実体化を使用した導出テーブル、ビュー参照および共通テーブル式の最適化」を参照してください。
-
エンジン条件プッシュダウンフラグ
詳細は、セクション8.2.1.5「エンジンコンディションプッシュダウンの最適化」を参照してください。
-
ハッシュ結合フラグ
詳細は、セクション8.2.1.4「ハッシュ結合の最適化」を参照してください。
-
インデックス条件プッシュダウンフラグ
詳細は、セクション8.2.1.6「インデックスコンディションプッシュダウンの最適化」を参照してください。
-
インデックス拡張フラグ
詳細は、セクション8.3.10「インデックス拡張の使用」を参照してください。
-
インデックスマージフラグ
詳細については、セクション8.2.1.3「インデックスマージの最適化」を参照してください。
-
インデックス可視性フラグ
詳細は、セクション8.3.12「不可視のインデックス」を参照してください。
-
制限最適化フラグ
-
prefer_ordering_index
(defaulton
)LIMIT
句を含むORDER BY
またはGROUP BY
を持つクエリーの場合に、オプティマイザが順序付けされていないインデックス、filesort またはその他の最適化のかわりに順序付けられたインデックスを使用しようとするかどうかを制御します。 この最適化は、オプティマイザがこの最適化を使用するとクエリーの実行速度が速くなると判断するたびに、デフォルトで実行されます。この決定を行うアルゴリズムではすべての同義のケースを処理できないため (データの分散が常に均一であるか、それほど均一でないことが前提となります)、この最適化が望ましくない場合があります。 MySQL 8.0.21 より前は、この最適化を無効にすることはできませんが、MySQL 8.0.21 以降ではデフォルトの動作のままですが、
prefer_ordering_index
フラグをoff
に設定することで無効にできます。
詳細および例については、セクション8.2.1.19「LIMIT クエリーの最適化」を参照してください。
-
-
複数範囲検針フラグ
詳細は、セクション8.2.1.11「Multi-Range Read の最適化」を参照してください。
-
準結合フラグ
semijoin
,firstmatch
,loosescan
およびduplicateweedout
フラグを使用すると、準結合戦略を制御できます。semijoin
フラグは、準結合を使用するかどうかを制御します。on
に設定されている場合、firstmatch
およびloosescan
フラグを使用すると、許可された準結合戦略をより細かく制御できます。duplicateweedout
準結合戦略が無効になっている場合は、他のすべての適用可能な戦略も無効にしないかぎり、使用されません。semijoin
とmaterialization
の両方がon
の場合、準結合でも実体化が使用されます (該当する場合)。 これらのフラグはデフォルトでon
です。詳細は、セクション8.2.2.1「準結合変換による IN および EXISTS サブクエリー述語の最適化」を参照してください。
-
スキャンフラグのスキップ
詳細は、スキャン範囲アクセス方法のスキップを参照してください。
-
サブクエリー実体化フラグ
materialization
フラグはサブクエリー実体化を使用するかどうかを制御します。semijoin
とmaterialization
の両方がon
の場合、準結合でも実体化が使用されます (該当する場合)。 これらのフラグはデフォルトでon
です。subquery_materialization_cost_based
フラグを使用すると、サブクエリー実体化とIN
からEXISTS
へのサブクエリー変換の選択を制御できます。 フラグがon
(デフォルト) の場合、オプティマイザはサブクエリー実体化とIN
- から -EXISTS
サブクエリー変換 (いずれかの方法を使用できる場合) の間でコストベースの選択を実行します。 フラグがoff
の場合、オプティマイザはIN
からEXISTS
へのサブクエリー変換よりもサブクエリーの実体化を選択します。詳細は、セクション8.2.2「サブクエリー、導出テーブル、ビュー参照および共通テーブル式の最適化」を参照してください。
-
サブクエリー変換フラグ
-
subquery_to_derived
(defaultoff
)MySQL 8.0.21 以降、オプティマイザは多くの場合、
SELECT
,WHERE
,JOIN
またはHAVING
句のスカラーサブクエリーを導出テーブルの左外部結合に変換できます。 (導出テーブルの NULL 値可能性によっては、内部結合にさらに簡略化される場合があります。) これは、次の条件を満たすサブクエリーに対して実行できます:サブクエリーでは、
RAND()
などの非決定的関数は使用されません。サブクエリーは、
MIN()
またはMAX()
を使用するようにリライトできるANY
またはALL
サブクエリーではありません。親クエリーはユーザー変数を設定しません。リライトすると実行順序に影響する可能性があるため、同じクエリーで変数に複数回アクセスすると、予期しない結果が発生する可能性があります。
サブクエリーは相関付けしないでください。つまり、外部クエリーのテーブルからカラムを参照したり、外部クエリーで評価される集計を含むことはできません。
MySQL 8.0.22 より前は、サブクエリーに
GROUP BY
句を含めることはできませんでした。この最適化は、
GROUP BY
を含まないIN
,NOT IN
,EXISTS
またはNOT EXISTS
の引数であるテーブルサブクエリーにも適用できます。このフラグのデフォルト値は
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
2 番目の
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 (
でクエリーを使用する例:subquery
)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 (
と前述の例と同じテーブルおよびデータを使用したクエリーを使用します:subquery
)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
subquery_to_derived
が有効になっているときに、クエリーSELECT * FROM t1 WHERE t1.b < 0 OR EXISTS(SELECT * FROM t2 WHERE t2.a = t1.a + 1)
でEXPLAIN
を実行した後にSHOW WARNINGS
を実行し、結果の 2 行目を簡略化すると、次のような形式でリライトされていることがわかります: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;
詳細は、セクション8.2.2.4「マージまたは実体化を使用した導出テーブル、ビュー参照および共通テーブル式の最適化」、セクション8.2.1.19「LIMIT クエリーの最適化」 および セクション8.2.2.1「準結合変換による 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
サーバーが特定のクエリーに対して Index Merge Union または Index Merge Sort-Union アクセス方法を使用しており、オプティマイザがそれらなしでパフォーマンスを向上できるかどうかを確認する場合は、次のように変数値を設定します:
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