メタデータの末尾にスキップ
メタデータの先頭に移動

クエリは複数の有効な実行プランを持ちえます。例えば、テーブルから行を読み込む時に選択できる幾つかのインデックスがあるかも知れません。Clustrixクエリオプティマイザーは最も効果的な実行プランを決定するために、それらの選択肢を検証します。

オプティマイザーはSQLクエリをオペレーターと呼ばれる構成ブロックのセットに変形します。それぞれのオペレータはインデックスをスキャンあるいは述語に基づいた行のフィルタのような基本的なオペレーションを実行します。オペレータはクエリ実行プランを木構造に配置され、木の根元はクエリによって返される可能性のある全ての行を表します。

クエリのために選択された実行プランを表示するには、EXPLAINコマンドを発行します。 

Explain コマンドの例
mysql> CREATE TABLE foo (a int, b int);
Query OK, 0 rows affected (0.17 sec)
 
mysql> INSERT INTO foo VALUES (5,10);
Query OK, 1 row affected (0.04 sec)
 
mysql> EXPLAIN SELECT * FROM foo WHERE a = 5;
+------------------------------------+-----------+-----------+
| Operation                          | Est. Cost | Est. Rows |
+------------------------------------+-----------+-----------+
| stream_combine                     |     31.72 |      0.91 |
|   filter (1.a = param(0))          |     10.21 |      0.30 |
|     index_scan 1 := foo.__base_foo |     10.20 |      0.34 |
+------------------------------------+-----------+-----------+
3 rows in set (0.00 sec)


Operation 欄は二つのオペレータをリスト表示します: filter および index_scan. index_scan エントリはフィルタの子であることを示すためにインデントされており、インデックススキャンによって作成された行はそのフィルタを経由して渡されることを意味します。"index_scan 1 := foo._base_foo" の行はインデックス base_foo はテーブル fooから読み込まれ、ユニークなID 1がこのオペレーションに割り当てられたことを意味します。このオペレーターは base_foo で見つかった全ての行を返します。fooが作成された時にプライマリキーが指定されなかったため、Clustrix DBMSは foo のプライマリインデックスのために隠れたプライマリキー、_base_fooを作成しました。
フィルタは述語 "a = 5" をパスする行だけを返します。

Clustrixクエリオプティマイザは 値5をパラメータに変換し、同じようなクエリがクエリの再コンパイル無しに同じ実行プランを使えるようにします。(例えば、再コンパイル無しに5は7または100に置き換えることができます)フィルタ内の述語は (1.a eq param((0 0)))として表現されます。1は _base_fooに割り当てられたユニークIDで、インデックス_base_fooa列を表します。最後の部分、param((0 0))は、パラメータ0 (内部的には二つ目の引数を足します)を表します。プランは、列 foo.a がパラメータ0 と等しい行を返します。

クエリの効率性を改善するために、列にインデックスを追加しその効果を検証します:

mysql> CREATE INDEX idx_a ON foo (a);
Query OK, 0 rows affected (0.73 sec)
 
 
mysql> EXPLAIN SELECT a FROM foo WHERE a = 5;
+-----------------------------------------+-----------+-----------+
| Operation                               | Est. Cost | Est. Rows |
+-----------------------------------------+-----------+-----------+
| index_scan 1 := foo.idx_a, a = param(0) |     10.61 |      1.01 |
+-----------------------------------------+-----------+-----------+
1 row in set (0.01 sec)


今度はクエリは新しいインデックスから読み込み、オプティマイザは述語をフィルタからインデックススキャンに押し込みました。フィルタはまだ表示されていますが、もうどのような処理も実行しません。
EXPLAINの出力のコストと行の欄は、実行プランの中の関係するオペレータの見積もりです。Est. Cost はコストに関係し、従ってミリ秒またはCPUサイクルのような単位で表示されていません。Est. Rows はオペレータによって返される行数の見積もりです。これらの見積もりはオペレータのプロパティに基づいており、オペレータの全ての子から返される行数です。

例えば、上の SELECT * FROM foo WHERE a = 5 のプランを考えてみます。

インデックススキャンで返される行の見積もりは、Clustrixがテーブルfoo とインデックス __base_foo のために保持している統計に基づいています。これらの統計は定期的に自動的にリフレッシュされます。(Clustrixに手動で強制して統計をリフレッシュすることはできません。)


もっと複雑な例があります:

mysql> CREATE TABLE bar (x int PRIMARY KEY, y int);
Query OK, 0 rows affected (0.15 sec)
 
 
mysql> INSERT INTO bar VALUES (1,10), (2,20), (3,20), (4,20);
Query OK, 4 rows affected (0.02 sec)
 
 
mysql> CREATE TABLE baz (r int);
Query OK, 0 rows affected (0.14 sec)
 
mysql> INSERT INTO baz VALUES (1), (1), (2), (9);
Query OK, 4 rows affected (0.02 sec)
 
 
mysql> EXPLAIN SELECT x, SUM(y) FROM bar JOIN baz ON x = r GROUP BY x;
+-----------------------------------------------------------------------+-----------+-----------+
| Operation                                                             | Est. Cost | Est. Rows |
+-----------------------------------------------------------------------+-----------+-----------+
| hash_aggregate_combine GROUPBY((1 . "x")) expr0 := sum((0 . "expr0")) |     81.80 |      4.00 |
|   hash_aggregate_partial GROUPBY((1 . "x")) expr0 := sum((1 . "y"))   |     76.20 |      4.00 |
|     nljoin                                                            |     76.20 |      4.00 |
|       stream_combine                                                  |     33.80 |      4.00 |
|         index_scan 2 := baz.__base_baz                                |     10.80 |      1.33 |
|       index_scan 1 := bar.__idx_bar__PRIMARY, x = 2.r                 |     10.60 |      1.00 |
+-----------------------------------------------------------------------+-----------+-----------+
6 rows in set (0.02 sec)


オペレータ nljoin は最初の入力の各行を取り、それを二つ目の入力の各行とjoinします。dual は良いjoinの順を強制する内部的なオペレータです。内側の nljoinbaz._base_baz のインデックスすきゃんと等価。外側の nljoinbaz.base_baz をまず読み込み、それから bar.idx_bar_PRIMARYとjoinします。後者のインデックススキャンの x = 2.r の存在は、シークオペレーションを示唆し、フルテーブルスキャンを避けることを意味します。

TOP
inserted by FC2 system