TiDB クエリ実行プランの概要
SQLは宣言型言語です。クエリの結果がどのようになるべきかを記述するものであり、実際に結果を取得する方法論を記述するものではありません。TiDBは、テーブルを結合する順序や、インデックスの使用可能性など、クエリの実行方法の可能性をすべて考慮します。クエリ実行プランを検討するプロセスは、SQL最適化と呼ばれます。
EXPLAIN文目は、特定の文に対して選択された実行プランを示します。つまり、TiDB は、クエリの実行方法を数百、数千通り検討した結果、このプランが最も少ないリソース消費量で、最短時間で実行されると判断します。
CREATE TABLE t (id INT NOT NULL PRIMARY KEY auto_increment, a INT NOT NULL, pad1 VARCHAR(255), INDEX(a));
INSERT INTO t VALUES (1, 1, 'aaa'),(2,2, 'bbb');
EXPLAIN SELECT * FROM t WHERE a = 1;
Query OK, 0 rows affected (0.96 sec)
Query OK, 2 rows affected (0.02 sec)
Records: 2 Duplicates: 0 Warnings: 0
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
| id | estRows | task | access object | operator info |
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
| IndexLookUp_10 | 10.00 | root | | |
| ├─IndexRangeScan_8(Build) | 10.00 | cop[tikv] | table:t, index:a(a) | range:[1,1], keep order:false, stats:pseudo |
| └─TableRowIDScan_9(Probe) | 10.00 | cop[tikv] | table:t | keep order:false, stats:pseudo |
+-------------------------------+---------+-----------+---------------------+---------------------------------------------+
3 rows in set (0.00 sec)
EXPLAIN実際のクエリを実行しません。2 EXPLAIN ANALYZEクエリを実行し、 EXPLAIN情報を表示します。これは、選択された実行プランが最適ではないケースを診断するのに役立ちます。6 EXPLAIN使用例については、以下のドキュメントをご覧ください。
EXPLAIN出力を理解する
以下は、上記のEXPLAINのステートメントの出力について説明しています。
id、SQL文の実行に必要な演算子またはサブタスクの名前を表します。詳細についてはオペレーターの概要参照してください。estRows、TiDB が処理すると予想される行数の推定値を示します。この数値は、アクセス方法が主キーまたは一意キーに基づいている場合など、辞書情報に基づく場合もあれば、CMSketch やヒストグラムなどの統計情報に基づく場合もあります。task作業者が作業を行っている場所を示します。詳細はタスクの概要ご覧ください。access object、アクセスされているテーブル、パーティション、およびインデックスを示します。また、上記の例ではインデックスの列aが使用されているため、インデックスの各部分も表示されます。これは、複合インデックスがある場合に役立ちます。operator infoアクセスに関する追加情報を表示します。詳細についてはオペレーター情報の概要ご覧ください。
オペレーターの概要
演算子とは、クエリ結果を返す際に実行される特定のステップです。テーブルスキャン(ディスクまたはTiKVブロックキャッシュ)を実行する演算子は以下のとおりです。
- TableFullScan : テーブル全体のスキャン
- TableRangeScan : 指定された範囲でテーブルをスキャンします
- TableRowIDScan : RowIDに基づいてテーブルデータをスキャンします。通常、インデックス読み取り操作の後に、一致するデータ行を取得します。
- IndexFullScan : テーブル データではなくインデックスがスキャンされる点を除いて、「フル テーブル スキャン」に似ています。
- IndexRangeScan : 指定された範囲でインデックスをスキャンします。
TiDBは、TiKV/ TiFlashからスキャンされたデータまたは計算結果を集約します。データ集約演算子は以下のカテゴリに分類できます。
- TableReader : TiKV の
TableFullScanやTableRangeScanの基礎となる演算子によって取得されたデータを集計します。 - IndexReader : TiKV の
IndexFullScanやIndexRangeScanの基礎となる演算子によって取得されたデータを集計します。 - IndexLookUp : まず、
Build側でスキャンされたRowID(TiKV内)を集計します。次に、Probe側でこれらのRowIDに基づいてTiKVからデータを正確に読み取ります。6BuildにはIndexFullScanやIndexRangeScanなどの演算子があり、Probe側にはTableRowIDScan演算子があります。 - IndexMerge :
IndexLookUpと同様です。4IndexMergeIndexLookupReaderの拡張と見なすことができます。8IndexMerge複数のインデックスの同時読み取りをサポートします。10 はBuildあり、Probeは1つです。14 の実行プロセスはIndexMergeIndexLookUp同じです。
構造はツリー構造のように見えますが、クエリの実行において子ノードが親ノードより先に完了している必要は必ずしもありません。TiDBはクエリ内並列処理をサポートしているため、より正確な表現は、子ノードが親ノードに流れ込むというものです。親ノード、子ノード、兄弟ノードの演算子によって、クエリの一部が並列実行される可能性があります。
前の例では、演算子├─IndexRangeScan_8(Build)はインデックスa(a)に一致する行の内部RowID検索します。次に、演算子└─TableRowIDScan_9(Probe)これらの行をテーブルから取得します。
範囲クエリ
WHERE / HAVING / ONの条件では、TiDB オプティマイザは主キークエリまたはインデックスキークエリによって返された結果を分析します。例えば、これらの条件には、 > 、 < 、 = 、 >= 、 <=などの数値型と日付型の比較演算子や、 LIKEなどの文字型の比較演算子が含まれる場合があります。
タスクの概要
現在、TiDBの計算タスクは、copタスクとルートタスクの2つのカテゴリに分類できます。タスク番号cop[tikv]場合、演算子はTiKVコプロセッサ内で実行されます。タスク番号がroot場合、演算子はTiDB内で完了します。
SQL最適化の目標の一つは、計算を可能な限りTiKVに委ねることです。TiKVのコプロセッサーは、組み込みSQL関数(集計関数とスカラー関数を含む)、SQL LIMIT演算、インデックススキャン、テーブルスキャンのほとんどをサポートしています。
オペレーター情報の概要
operator info 、どの条件をプッシュダウンできたかなどの有用な情報を表示できます。
range: [1,1]、クエリのwhere句の述語(a = 1)がTiKV(タスクはcop[tikv])にプッシュダウンされたことを示しています。keep order:false、このクエリのセマンティクスでは TiKV が結果を順番に返す必要がないことを示しています。クエリが順序付けを必要とするように変更された場合(例えばSELECT * FROM t WHERE a = 1 ORDER BY id)、この条件はkeep order:trueになります。stats:pseudo、estRowsに示されている推定値が正確ではない可能性があることを示しています。TiDB はバックグラウンド処理の一環として定期的に統計を更新します。ANALYZE TABLE tを実行して手動で更新することもできます。
EXPLAIN文の実行後、異なる演算子は異なる情報を出力します。オプティマイザヒントを使用してオプティマイザの動作を制御し、それによって物理演算子の選択を制御できます。例えば、 /*+ HASH_JOIN(t1, t2) */オプティマイザがHash Joinアルゴリズムを使用することを意味します。詳細については、 オプティマイザヒント参照してください。