オプティマイザーのヒント
TiDBは、MySQL5.7で導入されたコメントのような構文に基づくオプティマイザヒントをサポートしています。たとえば、一般的な構文の1つは/*+ HINT_NAME([t1_name [, t2_name] ...]) */
です。 TiDBオプティマイザが最適でないクエリプランを選択する場合は、オプティマイザヒントの使用をお勧めします。
ノート:
5.7.7より前のMySQLコマンドラインクライアントは、デフォルトでオプティマイザヒントを削除します。これらの以前のバージョンで
Hint
構文を使用する場合は、クライアントの起動時に--comments
オプションを追加します。例:mysql -h 127.0.0.1 -P 4000 -uroot --comments
。
構文
オプティマイザーのヒントでは大文字と小文字が区別されず、SQLステートメントのSELECT
、またはDELETE
キーワードに続く/*+ ... */
UPDATE
コメント内で指定されます。オプティマイザーヒントは現在、 INSERT
のステートメントではサポートされていません。
複数のヒントは、コンマで区切ることで指定できます。たとえば、次のクエリは3つの異なるヒントを使用します。
SELECT /*+ USE_INDEX(t1, idx1), HASH_AGG(), HASH_JOIN(t1) */ count(*) FROM t t1, t t2 WHERE t1.a = t2.b;
オプティマイザヒントがクエリ実行プランにどのように影響するかは、 EXPLAIN
とEXPLAIN ANALYZE
の出力で確認できます。
ヒントが正しくないか不完全であっても、ステートメントエラーは発生しません。これは、ヒントは、クエリの実行に対してセマンティックなヒント(提案)のみを持つことを目的としているためです。同様に、ヒントが適用できない場合、TiDBはせいぜい警告を返します。
ノート:
コメントが指定されたキーワードの後ろに続かない場合、それらは一般的なMySQLコメントとして扱われます。コメントは有効にならず、警告は報告されません。
現在、TiDBは、スコープが異なる2つのカテゴリのヒントをサポートしています。ヒントの最初のカテゴリは、 /*+ HASH_AGG() */
などのクエリブロックのスコープで有効になります。ヒントの2番目のカテゴリは、 /*+ MEMORY_QUOTA(1024 MB)*/
などのクエリ全体で有効になります。
ステートメント内の各クエリまたはサブクエリは、異なるクエリブロックに対応し、各クエリブロックには独自の名前があります。例えば:
SELECT * FROM (SELECT * FROM t) t1, (SELECT * FROM t) t2;
上記のクエリステートメントには3つのクエリブロックがあります。最も外側のSELECT
は、名前がsel_1
の最初のクエリブロックに対応します。 2つのSELECT
つのサブクエリは、2番目と3番目のクエリブロックに対応します。名前はそれぞれsel_2
とsel_3
です。番号の順序は、左から右へのSELECT
の出現に基づいています。最初のSELECT
をDELETE
またはUPDATE
に置き換えると、対応するクエリブロック名はdel_1
またはupd_1
になります。
クエリブロックで有効になるヒント
このカテゴリのヒントは、 SELECT
、またはDELETE
UPDATE
のキーワードの後ろに続けることができます。ヒントの有効範囲を制御するには、ヒントでクエリブロックの名前を使用します。クエリ内の各テーブルを正確に識別することで、ヒントパラメータを明確にすることができます(テーブル名またはエイリアスが重複している場合)。ヒントにクエリブロックが指定されていない場合、ヒントはデフォルトで現在のブロックで有効になります。
例えば:
SELECT /*+ HASH_JOIN(@sel_1 t1@sel_1, t3) */ * FROM (SELECT t1.a, t1.b FROM t t1, t t2 WHERE t1.a = t2.a) t1, t t3 WHERE t1.b = t3.b;
このヒントはsel_1
のクエリブロックで有効になり、そのパラメータはsel_1
のt1
とt3
のテーブルです( sel_2
にはt1
のテーブルも含まれます)。
上記のように、次の方法でヒントにクエリブロックの名前を指定できます。
- ヒントの最初のパラメーターとしてクエリブロック名を設定し、他のパラメーターとはスペースで区切ります。
QB_NAME
に加えて、このセクションにリストされているすべてのヒントには、別のオプションの非表示パラメーター@QB_NAME
もあります。このパラメーターを使用することにより、このヒントの有効な範囲を指定できます。 - パラメータのテーブル名に
@QB_NAME
を追加して、このテーブルが属するクエリブロックを明示的に指定します。
ノート:
ヒントを有効にするクエリブロックの中または前にヒントを配置する必要があります。ヒントがクエリブロックの後に置かれている場合、それは有効になりません。
QB_NAME
クエリステートメントが複数のネストされたクエリを含む複雑なステートメントである場合、特定のクエリブロックのIDと名前が誤って識別される可能性があります。ヒントQB_NAME
は、この点で役立ちます。
QB_NAME
はクエリブロック名を意味します。クエリブロックに新しい名前を指定できます。指定されたQB_NAME
と以前のデフォルト名は両方とも有効です。例えば:
SELECT /*+ QB_NAME(QB1) */ * FROM (SELECT * FROM t) t1, (SELECT * FROM t) t2;
このヒントは、外側のSELECT
クエリブロックの名前をQB1
に指定します。これにより、 QB1
とデフォルト名sel_1
の両方がクエリブロックに対して有効になります。
ノート:
上記の例で、ヒントが
QB_NAME
からsel_2
を指定し、元の2番目のSELECT
クエリブロックに新しいQB_NAME
を指定しない場合、sel_2
は2番目のSELECT
クエリブロックの無効な名前になります。
MERGE_JOIN(t1_name [、tl_name ...])
MERGE_JOIN(t1_name [, tl_name ...])
ヒントは、オプティマイザに、指定されたテーブルに対してソート-マージ結合アルゴリズムを使用するように指示します。一般に、このアルゴリズムはより少ないメモリを消費しますが、より長い処理時間を要します。データ量が非常に多い場合やシステムメモリが不足している場合は、このヒントを使用することをお勧めします。例えば:
select /*+ MERGE_JOIN(t1, t2) */ * from t1,t2 where t1.id = t2.id;
ノート:
TIDB_SMJ
は、TiDB3.0.x以前のバージョンのMERGE_JOIN
のエイリアスです。これらのバージョンのいずれかを使用している場合は、ヒントにTIDB_SMJ(t1_name [, tl_name ...])
の構文を適用する必要があります。それ以降のバージョンのTiDBでは、TIDB_SMJ
とMERGE_JOIN
の両方がヒントの有効な名前ですが、MERGE_JOIN
をお勧めします。
INL_JOIN(t1_name [、tl_name ...])
INL_JOIN(t1_name [, tl_name ...])
ヒントは、指定されたテーブルにインデックスネストループ結合アルゴリズムを使用するようにオプティマイザに指示します。このアルゴリズムは、一部のシナリオではシステムリソースの消費量が少なく、処理時間が短くなる可能性があり、他のシナリオでは逆の結果をもたらす可能性があります。外側のテーブルがWHERE
条件でフィルタリングされた後、結果セットが10,000行未満の場合は、このヒントを使用することをお勧めします。例えば:
select /*+ INL_JOIN(t1, t2) */ * from t1,t2 where t1.id = t2.id;
INL_JOIN()
で指定されたパラメーターは、クエリプランを作成するときの内部テーブルの候補テーブルです。たとえば、 INL_JOIN(t1)
は、TiDBがクエリプランを作成するための内部テーブルとしてt1
の使用のみを考慮することを意味します。候補テーブルにエイリアスがある場合は、 INL_JOIN()
のパラメータとしてエイリアスを使用する必要があります。エイリアスがない場合は、テーブルの元の名前をパラメータとして使用します。たとえば、 select /*+ INL_JOIN(t1) */ * from t t1, t t2 where t1.a = t2.b;
クエリでは、 INL_JOIN()
のパラメータとしてt
ではなくt
テーブルのエイリアスt1
またはt2
を使用する必要があります。
ノート:
TIDB_INLJ
は、TiDB3.0.x以前のバージョンのINL_JOIN
のエイリアスです。これらのバージョンのいずれかを使用している場合は、ヒントにTIDB_INLJ(t1_name [, tl_name ...])
の構文を適用する必要があります。それ以降のバージョンのTiDBでは、TIDB_INLJ
とINL_JOIN
の両方がヒントの有効な名前ですが、INL_JOIN
をお勧めします。
INL_HASH_JOIN
INL_HASH_JOIN(t1_name [, tl_name])
ヒントは、オプティマイザにインデックスネストループハッシュ結合アルゴリズムを使用するように指示します。このアルゴリズムを使用するための条件は、インデックスネストループ結合アルゴリズムを使用するための条件と同じです。 2つのアルゴリズムの違いは、 INL_JOIN
は結合された内部テーブルにハッシュテーブルを作成しますが、 INL_HASH_JOIN
は結合された外部テーブルにハッシュテーブルを作成することです。 INL_HASH_JOIN
にはメモリ使用量の固定制限がありますが、 INL_JOIN
で使用されるメモリは、内部テーブルで一致する行数によって異なります。
HASH_JOIN(t1_name [、tl_name ...])
HASH_JOIN(t1_name [, tl_name ...])
ヒントは、指定されたテーブルにハッシュ結合アルゴリズムを使用するようにオプティマイザーに指示します。このアルゴリズムを使用すると、クエリを複数のスレッドと同時に実行できます。これにより、処理速度は向上しますが、より多くのメモリを消費します。例えば:
select /*+ HASH_JOIN(t1, t2) */ * from t1,t2 where t1.id = t2.id;
ノート:
TIDB_HJ
は、TiDB3.0.x以前のバージョンのHASH_JOIN
のエイリアスです。これらのバージョンのいずれかを使用している場合は、ヒントにTIDB_HJ(t1_name [, tl_name ...])
の構文を適用する必要があります。それ以降のバージョンのTiDBでは、TIDB_HJ
とHASH_JOIN
の両方がヒントの有効な名前ですが、HASH_JOIN
をお勧めします。
HASH_AGG()
HASH_AGG()
ヒントは、指定されたクエリブロック内のすべての集計関数でハッシュ集計アルゴリズムを使用するようにオプティマイザーに指示します。このアルゴリズムを使用すると、クエリを複数のスレッドと同時に実行できます。これにより、処理速度は向上しますが、より多くのメモリを消費します。例えば:
select /*+ HASH_AGG() */ count(*) from t1,t2 where t1.a > 10 group by t1.id;
STREAM_AGG()
STREAM_AGG()
ヒントは、指定されたクエリブロック内のすべての集計関数でストリーム集計アルゴリズムを使用するようにオプティマイザに指示します。一般に、このアルゴリズムはより少ないメモリを消費しますが、より長い処理時間を要します。データ量が非常に多い場合やシステムメモリが不足している場合は、このヒントを使用することをお勧めします。例えば:
select /*+ STREAM_AGG() */ count(*) from t1,t2 where t1.a > 10 group by t1.id;
USE_INDEX(t1_name、idx1_name [、idx2_name ...])
USE_INDEX(t1_name, idx1_name [, idx2_name ...])
ヒントは、指定されたt1_name
のテーブルに対して指定されたインデックスのみを使用するようにオプティマイザに指示します。たとえば、次のヒントを適用すると、 select * from t t1 use index(idx1, idx2);
ステートメントを実行するのと同じ効果があります。
SELECT /*+ USE_INDEX(t1, idx1, idx2) */ * FROM t1;
ノート:
このヒントでテーブル名のみを指定し、インデックス名は指定しない場合、実行ではインデックスは考慮されず、テーブル全体がスキャンされます。
IGNORE_INDEX(t1_name、idx1_name [、idx2_name ...])
IGNORE_INDEX(t1_name, idx1_name [, idx2_name ...])
ヒントは、指定されたt1_name
テーブルの指定されたインデックスを無視するようにオプティマイザーに指示します。たとえば、次のヒントを適用すると、 select * from t t1 ignore index(idx1, idx2);
ステートメントを実行するのと同じ効果があります。
select /*+ IGNORE_INDEX(t1, idx1, idx2) */ * from t t1;
AGG_TO_COP()
AGG_TO_COP()
ヒントは、指定されたクエリ・ブロック内の集約操作をコプロセッサーにプッシュダウンするようにオプティマイザーに指示します。オプティマイザがプッシュダウンに適した集計関数をプッシュダウンしない場合は、このヒントを使用することをお勧めします。例えば:
select /*+ AGG_TO_COP() */ sum(t1.a) from t t1;
LIMIT_TO_COP()
LIMIT_TO_COP()
ヒントは、指定されたクエリブロック内のLimit
およびTopN
の演算子をコプロセッサーにプッシュダウンするようにオプティマイザーに指示します。オプティマイザがそのような操作を実行しない場合は、このヒントを使用することをお勧めします。例えば:
SELECT /*+ LIMIT_TO_COP() */ * FROM t WHERE a = 1 AND b > 10 ORDER BY c LIMIT 1;
READ_FROM_STORAGE(TIFLASH [t1_name [、tl_name ...]]、TIKV [t2_name [、tl_name ...]])
READ_FROM_STORAGE(TIFLASH[t1_name [, tl_name ...]], TIKV[t2_name [, tl_name ...]])
ヒントは、オプティマイザーに特定のストレージエンジンから特定のテーブルを読み取るように指示します。現在、このヒントは2つのストレージエンジンパラメータ( TIKV
とTIFLASH
)をサポートしています。テーブルにエイリアスがある場合は、エイリアスをREAD_FROM_STORAGE()
のパラメータとして使用します。テーブルにエイリアスがない場合は、テーブルの元の名前をパラメータとして使用します。例えば:
select /*+ READ_FROM_STORAGE(TIFLASH[t1], TIKV[t2]) */ t1.a from t t1, t t2 where t1.a = t2.a;
ノート:
オプティマイザで別のスキーマのテーブルを使用する場合は、スキーマ名を明示的に指定する必要があります。例えば:
SELECT /*+ READ_FROM_STORAGE(TIFLASH[test1.t1,test2.t2]) */ t1.a FROM test1.t t1, test2.t t2 WHERE t1.a = t2.a;
USE_INDEX_MERGE(t1_name、idx1_name [、idx2_name ...])
USE_INDEX_MERGE(t1_name, idx1_name [, idx2_name ...])
ヒントは、オプティマイザにインデックスマージメソッドを使用して特定のテーブルにアクセスするように指示します。指定されたインデックスのリストはオプションのパラメータです。リストを明示的に指定すると、TiDBはリストからインデックスを選択してインデックスマージを構築します。インデックスのリストを指定しない場合、TiDBは使用可能なすべてのインデックスからインデックスを選択してインデックスマージを構築します。例えば:
SELECT /*+ USE_INDEX_MERGE(t1, idx_a, idx_b, idx_c) */ * FROM t1 WHERE t1.a > 10 OR t1.b > 10;
同じテーブルに対して複数のUSE_INDEX_MERGE
ヒントが作成されると、オプティマイザは、これらのヒントで指定されたインデックスセットの和集合からインデックスを選択しようとします。
ノート:
USE_INDEX_MERGE
のパラメーターは、列名ではなく、索引名を参照します。主キーのインデックス名はprimary
です。
このヒントは、次のような厳しい条件で有効になります。
- クエリが全表スキャンに加えて単一のインデックススキャンを選択できる場合、オプティマイザはインデックスマージを選択しません。
クエリ全体で有効になるヒント
このカテゴリのヒントは、最初のSELECT
、またはUPDATE
キーワードの後ろにのみ続くことができDELETE
。これは、このクエリの実行時に指定されたシステム変数の値を変更することと同じです。ヒントの優先度は、既存のシステム変数の優先度よりも高くなっています。
ノート:
このカテゴリのヒントには、オプションの非表示変数
@QB_NAME
もありますが、変数を指定した場合でも、ヒントはクエリ全体で有効になります。
NO_INDEX_MERGE()
NO_INDEX_MERGE()
ヒントは、オプティマイザのインデックスマージ機能を無効にします。
たとえば、次のクエリはインデックスマージを使用しません。
select /*+ NO_INDEX_MERGE() */ * from t where t.a > 0 or t.b > 0;
このヒントに加えて、 tidb_enable_index_merge
システム変数を設定すると、この機能を有効にするかどうかも制御されます。
ノート:
NO_INDEX_MERGE
はUSE_INDEX_MERGE
よりも優先度が高くなります。両方のヒントを使用すると、USE_INDEX_MERGE
は有効になりません。- サブクエリの場合、
NO_INDEX_MERGE
は、サブクエリの最も外側のレベルに配置されている場合にのみ有効になります。
USE_TOJA(boolean_value)
boolean_value
パラメーターはTRUE
またはFALSE
にすることができます。 USE_TOJA(TRUE)
ヒントを使用すると、オプティマイザはin
条件(サブクエリを含む)を結合および集計操作に変換できます。比較すると、 USE_TOJA(FALSE)
ヒントはこの機能を無効にします。
たとえば、次のクエリはin (select t2.a from t2) subq
を対応する結合および集計操作に変換します。
select /*+ USE_TOJA(TRUE) */ t1.a, t1.b from t1 where t1.a in (select t2.a from t2) subq;
このヒントに加えて、 tidb_opt_insubq_to_join_and_agg
システム変数を設定すると、この機能を有効にするかどうかも制御されます。
MAX_EXECUTION_TIME(N)
MAX_EXECUTION_TIME(N)
ヒントは、サーバーがステートメントを終了する前にステートメントの実行が許可される期間に制限N
(ミリ秒単位のタイムアウト値)を設定します。次のヒントで、 MAX_EXECUTION_TIME(1000)
は、タイムアウトが1000ミリ秒(つまり、1秒)であることを意味します。
select /*+ MAX_EXECUTION_TIME(1000) */ * from t1 inner join t2 where t1.id = t2.id;
このヒントに加えて、 global.max_execution_time
システム変数はステートメントの実行時間を制限することもできます。
MEMORY_QUOTA(N)
MEMORY_QUOTA(N)
のヒントでは、ステートメントで使用できるメモリの量に制限N
(MBまたはGB単位のしきい値)が設定されます。ステートメントのメモリ使用量がこの制限を超えると、TiDBはステートメントの制限を超えた動作に基づいてログメッセージを生成するか、単に終了します。
次のヒントで、 MEMORY_QUOTA(1024 MB)
は、メモリ使用量が1024MBに制限されていることを意味します。
select /*+ MEMORY_QUOTA(1024 MB) */ * from t;
このヒントに加えて、 tidb_mem_quota_query
システム変数はステートメントのメモリ使用量を制限することもできます。
READ_CONSISTENT_REPLICA()
READ_CONSISTENT_REPLICA()
ヒントは、TiKVフォロワーノードから一貫性のあるデータを読み取る機能を有効にします。例えば:
select /*+ READ_CONSISTENT_REPLICA() */ * from t;
このヒントに加えて、 tidb_replica_read
環境変数を'follower'
または'leader'
に設定すると、この機能を有効にするかどうかも制御されます。
IGNORE_PLAN_CACHE()
IGNORE_PLAN_CACHE()
のヒントは、現在のprepare
のステートメントを処理するときにプランキャッシュを使用しないようにオプティマイザーに通知します。
このヒントは、 実行プランキャッシュつの機能が有効になっている場合に、特定のタイプのクエリのプランキャッシュを一時的に無効にするために使用されます。
次の例では、 prepare
ステートメントを実行すると、プランキャッシュが強制的に無効になります。
prepare stmt from 'select /*+ IGNORE_PLAN_CACHE() */ * from t where t.id = ?';
NTH_PLAN(N)
NTH_PLAN(N)
のヒントは、物理最適化中に検出されたN
番目の物理計画を選択するようにオプティマイザーに通知します。 N
は正の整数でなければなりません。
指定されたN
が物理最適化の検索範囲を超えている場合、TiDBは警告を返し、このヒントを無視する戦略に基づいて最適な物理計画を選択します。
このヒントは、カスケードプランナーが有効になっている場合は有効になりません。
次の例では、オプティマイザーは、物理最適化中に検出された3番目の物理計画を選択するように強制されます。
SELECT /*+ NTH_PLAN(3) */ count(*) from t where a > 5;
ノート:
NTH_PLAN(N)
は主にテストに使用され、それ以降のバージョンでは互換性が保証されません。このヒントは注意して使用してください。