インデックス マージを使用したステートメントの説明
インデックス マージは、テーブルにアクセスするために TiDB v4.0 で導入された方法です。この方法を使用すると、TiDB オプティマイザーはテーブルごとに複数のインデックスを使用し、各インデックスから返された結果をマージできます。一部のシナリオでは、この方法を使用すると、テーブル全体のスキャンが回避されるため、クエリがより効率的になります。
TiDB におけるインデックスのマージには、交差型と共用体型の 2 種類があります。前者は式AND
に、後者は式OR
に適用されます。ユニオン型のインデックス マージは、TiDB v4.0 で実験的機能として導入され、v5.4.0 で GA になりました。交差タイプは TiDB v6.5.0 で導入され、 USE_INDEX_MERGE
ヒントが指定されている場合にのみ使用できます。
インデックス マージを有効にする
v5.4.0 以降の TiDB バージョンでは、デフォルトでインデックス マージが有効になっています。その他の状況では、インデックス マージが有効になっていない場合、変数tidb_enable_index_merge
からON
を設定してこの機能を有効にする必要があります。
SET session tidb_enable_index_merge = ON;
例
CREATE TABLE t(a int, b int, c int, d int, INDEX idx_a(a), INDEX idx_b(b), INDEX idx_c(c), INDEX idx_d(d));
EXPLAIN SELECT /*+ NO_INDEX_MERGE() */ * FROM t WHERE a = 1 OR b = 1;
+-------------------------+----------+-----------+---------------+--------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------+----------+-----------+---------------+--------------------------------------+
| TableReader_7 | 19.99 | root | | data:Selection_6 |
| └─Selection_6 | 19.99 | cop[tikv] | | or(eq(test.t.a, 1), eq(test.t.b, 1)) |
| └─TableFullScan_5 | 10000.00 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------+----------+-----------+---------------+--------------------------------------+
EXPLAIN SELECT /*+ USE_INDEX_MERGE(t) */ * FROM t WHERE a > 1 OR b > 1;
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| IndexMerge_8 | 5555.56 | root | | type: union |
| ├─IndexRangeScan_5(Build) | 3333.33 | cop[tikv] | table:t, index:idx_a(a) | range:(1,+inf], keep order:false, stats:pseudo |
| ├─IndexRangeScan_6(Build) | 3333.33 | cop[tikv] | table:t, index:idx_b(b) | range:(1,+inf], keep order:false, stats:pseudo |
| └─TableRowIDScan_7(Probe) | 5555.56 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
前のクエリでは、フィルター条件はWHERE
句で、結合子としてOR
を使用します。インデックス マージを使用しない場合、テーブルごとに 1 つのインデックスしか使用できません。 a = 1
インデックスa
にプッシュすることはできません。 b = 1
インデックスb
にプッシュすることもできません。 t
に大量のデータが存在する場合、全表スキャンは非効率的です。このようなシナリオを処理するために、テーブルにアクセスするためのインデックス マージが TiDB に導入されました。
前のクエリの場合、オプティマイザはユニオン タイプのインデックス マージを選択してテーブルにアクセスします。インデックス マージにより、オプティマイザはテーブルごとに複数のインデックスを使用し、各インデックスによって返された結果をマージし、前の出力で後者の実行計画を生成できます。
出力では、 operator info
of IndexMerge_8
演算子のtype: union
情報は、この演算子がユニオン タイプのインデックス マージであることを示しています。 3 つの子ノードがあります。 IndexRangeScan_5
とIndexRangeScan_6
範囲に従って条件を満たすRowID
秒をスキャンし、 TableRowIDScan_7
オペレーターはこれらのRowID
秒に従って条件を満たすすべてのデータを正確に読み取ります。
IndexRangeScan
/ TableRangeScan
などの特定の範囲のデータに対して実行されるスキャン操作の場合、結果のoperator info
列には、 IndexFullScan
/ TableFullScan
などの他のスキャン操作と比較して、スキャン範囲に関する追加情報が含まれます。上記の例では、 IndexRangeScan_13
演算子のrange:(1,+inf]
、演算子が 1 から正の無限大までデータをスキャンすることを示します。
EXPLAIN SELECT /*+ NO_INDEX_MERGE() */ * FROM t WHERE a > 1 AND b > 1 AND c = 1; -- Does not use index merge
+--------------------------------+---------+-----------+-------------------------+---------------------------------------------+
| id | estRows | task | access object | operator info |
+--------------------------------+---------+-----------+-------------------------+---------------------------------------------+
| IndexLookUp_19 | 1.11 | root | | |
| ├─IndexRangeScan_16(Build) | 10.00 | cop[tikv] | table:t, index:idx_c(c) | range:[1,1], keep order:false, stats:pseudo |
| └─Selection_18(Probe) | 1.11 | cop[tikv] | | gt(test.t.a, 1), gt(test.t.b, 1) |
| └─TableRowIDScan_17 | 10.00 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+--------------------------------+---------+-----------+-------------------------+---------------------------------------------+
EXPLAIN SELECT /*+ USE_INDEX_MERGE(t, idx_a, idx_b, idx_c) */ * FROM t WHERE a > 1 AND b > 1 AND c = 1; -- Uses index merge
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
| IndexMerge_9 | 1.11 | root | | type: intersection |
| ├─IndexRangeScan_5(Build) | 3333.33 | cop[tikv] | table:t, index:idx_a(a) | range:(1,+inf], keep order:false, stats:pseudo |
| ├─IndexRangeScan_6(Build) | 3333.33 | cop[tikv] | table:t, index:idx_b(b) | range:(1,+inf], keep order:false, stats:pseudo |
| ├─IndexRangeScan_7(Build) | 10.00 | cop[tikv] | table:t, index:idx_c(c) | range:[1,1], keep order:false, stats:pseudo |
| └─TableRowIDScan_8(Probe) | 1.11 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
前の例から、フィルタ条件がコネクタとしてAND
を使用するWHERE
句であることがわかります。インデックス マージが有効になる前は、オプティマイザーは 3 つのインデックス ( idx_a
、 idx_b
、またはidx_c
) のうちの 1 つしか選択できません。
フィルター条件のいずれかの選択性が低い場合、オプティマイザーは対応するインデックスを直接選択して、理想的な実行効率を実現します。ただし、データ分布が次の 3 つの条件をすべて満たしている場合は、交差型インデックス マージの使用を検討できます。
- テーブル全体のデータサイズが大きく、テーブル全体を直接読み込むのは非効率です。
- 3 つのフィルター条件のそれぞれについて、それぞれの選択性が非常に高いため、単一のインデックスを使用する
IndexLookUp
の実行効率は理想的ではありません。 - 3 つのフィルター条件の全体的な選択性は低くなります。
交差タイプのインデックス マージを使用してテーブルにアクセスする場合、オプティマイザーは、テーブルで複数のインデックスを使用することを選択し、各インデックスによって返された結果をマージして、前の出力例の後者のIndexMerge
実行プランを生成できます。 operator info
of IndexMerge_9
演算子のtype: intersection
情報は、この演算子が交差タイプのインデックス マージであることを示します。実行計画の他の部分は、前述のユニオン タイプのインデックス マージの例と同様です。
ノート:
インデックス マージ機能は、v5.4.0 からデフォルトで有効になっています。つまり、
tidb_enable_index_merge
はON
です。
tidb_enable_index_merge
の設定に関係なく、SQL ヒントUSE_INDEX_MERGE
使用して、オプティマイザーにインデックス マージを強制的に適用させることができます。フィルター条件にプッシュ ダウンできない式が含まれている場合にインデックス マージを有効にするには、SQL ヒントUSE_INDEX_MERGE
を使用する必要があります。オプティマイザーがクエリ プランに対して単一のインデックス スキャン方法 (全テーブル スキャン以外) を選択できる場合、オプティマイザーは自動的にインデックス マージを使用しません。オプティマイザがインデックス マージを使用するには、オプティマイザ ヒントを使用する必要があります。
現時点では、インデックス マージは一時テーブルではサポートされていません。
交差タイプの索引マージは、オプティマイザーによって自動的に選択されることはありません。選択するには、
USE_INDEX_MERGE
ヒントを使用してテーブル名とインデックス名を指定する必要があります。