インデックスマージを使用したステートメントの説明
インデックス マージは、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                 |
+-------------------------------+---------+-----------+-------------------------+------------------------------------------------+
前述のクエリでは、フィルター条件はORコネクターとして使用するWHERE句です。インデックス マージを使用しない場合、テーブルごとに 1 つのインデックスしか使用できません。 a = 1インデックスaにプッシュダウンすることはできません。また、 b = 1をインデックスbにプッシュダウンすることもできません。 tに大量のデータが存在する場合、フル テーブル スキャンは非効率的です。このようなシナリオに対処するために、TiDB ではテーブルにアクセスするためのインデックス マージが導入されています。
上記のクエリでは、オプティマイザはテーブルにアクセスするためにユニオンタイプのインデックス マージを選択します。インデックス マージにより、オプティマイザはテーブルごとに複数のインデックスを使用し、各インデックスによって返された結果をマージして、上記の出力の後者の実行プランを生成できます。
出力では、 IndexMerge_8演算子のoperator info type: union情報は、この演算子がユニオン型インデックス マージであることを示しています。3 つの子ノードがありますIndexRangeScan_5とIndexRangeScan_6 、範囲に従って条件を満たすRowIDをスキャンし、次にTableRowIDScan_7演算子は、これらのRowIDに従って条件を満たすすべてのデータを正確に読み取ります。
IndexRangeScan / TableRangeScanなどの特定のデータ範囲に対して実行されるスキャン操作の場合、結果のoperator info列には、 IndexFullScan / TableFullScanなどの他のスキャン操作と比較して、スキャン範囲に関する追加情報が含まれます。上記の例では、 IndexRangeScan_5演算子の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の実行プランを生成しますIndexMerge_9演算子のoperator infoのtype: intersection情報は、この演算子が交差型インデックス マージであることを示しています。実行プランのその他の部分は、前のユニオン型インデックス マージの例と同様です。
注記:
インデックスマージ機能は、v5.4.0 からデフォルトで有効になっています。つまり、
tidb_enable_index_mergeはONです。SQL ヒント
USE_INDEX_MERGEを使用すると、tidb_enable_index_mergeの設定に関係なく、オプティマイザにインデックス マージを適用させることができます。フィルタリング条件にプッシュダウンできない式が含まれている場合にインデックス マージを有効にするには、SQL ヒントUSE_INDEX_MERGEを使用する必要があります。オプティマイザがクエリ プランに対して単一インデックス スキャン メソッド (フル テーブル スキャン以外) を選択できる場合、オプティマイザはインデックス マージを自動的に使用しません。オプティマイザがインデックス マージを使用するには、オプティマイザ ヒントを使用する必要があります。
インデックスマージは現時点では一時テーブルではサポートされていません。
交差型インデックス マージは、オプティマイザによって自動的に選択されません。選択されるようにするには、
USE_INDEX_MERGEヒントを使用してテーブル名とインデックス名を指定する必要があります。