PostgreSQL のエクゼキュータによるノードの処理順はマニュアルに解説されています。
50.6. エクゼキュータ
この説明をかみ砕いて説明します。
エクゼキュータはプランナ/オプティマイザで作成された計画に従って処理を進めます。その際、上位ノードの要求に応じて下位ノードはタプル(行)を返します。例えば、Sort ノードを実行するためにはソート対象のタプルが必要となります。そこで、Sort ノードの下位ノードは Sort ノードの要求に応じてタプルを渡します。
マニュアルの記載に近い具体例で説明します。
=# CREATE TABLE tab1(c1 INTEGER); =# CREATE TABLE tab2(c1 INTEGER); =# INSERT INTO tab1 SELECT generate_series(1, 3); =# INSERT INTO tab2 SELECT generate_series(1, 3); =# VACUUM ANALYZE; =# SET enable_hashjoin TO OFF; =# EXPLAIN SELECT tab1.c1 FROM tab1 INNER JOIN tab2 ON tab1.c1 = tab2.c1; QUERY PLAN ---------------------------------------------------------------- Merge Join (cost=2.11..2.17 rows=3 width=4) Merge Cond: (tab1.c1 = tab2.c1) -> Sort (cost=1.05..1.06 rows=3 width=4) Sort Key: tab1.c1 -> Seq Scan on tab1 (cost=0.00..1.03 rows=3 width=4) -> Sort (cost=1.05..1.06 rows=3 width=4) Sort Key: tab2.c1 -> Seq Scan on tab2 (cost=0.00..1.03 rows=3 width=4) (8 rows)
この計画の最上位ノードは Merge Join ノードです。マージ結合は結合キーでソートしてから順番に突き合わせて結合する方法です。このため、tab1 / tab2 テーブルはそれぞれ結合キーでソートされています。そして、ソートするタプルを Seq Scan ノードで取得します。
マニュアルの記載に従って処理が進むとすれば、以下の動作になります。
- Merge Join ノードが tab1 の結合キーでソートされた最初のタプルを Sort ノードに要求する
- Sort ノードが tab1 のタプルを要求する(全てのタプルを取得するまで繰り返す)
- SeqScan ノードが tab1 のタプルを 1 つ返す
- Sort ノードが tab1 のタプルを結合キーでソートする
- Sort ノードが tab1 の結合キーでソートされた最初のタプルを返す(ソート結果は後の要求に備えて残す)
- Sort ノードが tab1 のタプルを要求する(全てのタプルを取得するまで繰り返す)
- Merge Join ノードが tab2 の結合キーでソートされた最初のタプルを Sort ノードに要求する
- Sort ノードが tab2 のタプルを要求する(全てのタプルを取得するまで繰り返す)
- SeqScan ノードが tab2 のタプルを 1 つ返す
- Sort ノードが tab2 のタプルを結合キーでソートする
- Sort ノードが tab2 の結合キーでソートされた最初のタプルを返す(ソート結果は後の要求に備えて残す)
- Sort ノードが tab2 のタプルを要求する(全てのタプルを取得するまで繰り返す)
- Merge Join ノードが tab1 と tab2 の最初のタプルで結合できるかを比較する
- 結合できれば呼び出し側に結果を返す
- Merge Join ノードは tab1 と tab2 の残りのタプルについて結合できるかを比較する
- 各 Sort ノードは残したソート結果から Merge Join ノードの要求に応じてタプルを返す
この処理の流れからわかるのが、各ノードは処理したタプルを上位ノードに順次渡しているということです。各ノードが処理を完了させてから次のノードの処理に移るわけではありません。
エクゼキュータの処理順を理解することで、実行計画から処理の流れをイメージしやすくなります。本記事が実行計画を読み解く際のヒントになれば幸いです。