あるクエリの最速の実行戦略がパラレルクエリであるとオプティマイザが決定すると、GatherまたはGather Mergeノードを含むクエリプランを作成します。 単純な例を示します。
EXPLAIN SELECT * FROM pgbench_accounts WHERE filler LIKE '%x%'; QUERY PLAN ------------------------------------------------------------------------------------- Gather (cost=1000.00..217018.43 rows=1 width=97) Workers Planned: 2 -> Parallel Seq Scan on pgbench_accounts (cost=0.00..216018.33 rows=1 width=97) Filter: (filler ~~ '%x%'::text) (4 rows)
どの場合でも、Gather
またはGather Merge
ノードは、正確に一つの子ノードを持ちます。
子プランは、プランの中で並列に実行される部分です。
Gather
またはGather Merge
ノードがプランツリーの中で最上位にある場合は、クエリ全体が並列に実行されます。
Gather
またはGather Merge
ノードがプランツリーの他の部分にある場合は、その部分だけが並列に実行されます。
上の例では、クエリはただ一つのテーブルにアクセスするので、Gather
ノード自身以外では、たった一つのプランノードだけが存在します。
そのプランノードはGather
ノードの子ノードなので、並列に実行されます。
EXPLAINを使って、プランナが選択したワーカーの数を見ることができます。
クエリの実行中にGather
ノードに到達すると、ユーザのセッションに対応しているプロセスは、プランナが選択したワーカーと同じ数のバックグラウンドワーカープロセスを要求します。
プランナが使用を検討するバックグラウンドワーカーの数は、最大でもmax_parallel_workers_per_gatherに制限されます。
ある時点で存在できるバックグラウンドワーカーの数は、max_worker_processesとmax_parallel_workersの両方を満たすように制限されます。
ですから、あるパラレルクエリが、プラン時よりも少ない数のワーカープロセスによって実行されたり、まったくワーカープロセスなしに実行されることがあり得ます。
最適なプランは利用可能なワーカーの数に依存することもあるので、これは低い性能をもたらす結果になるかもしれません。
これがしばしば起こるようなら、max_worker_processes
とmax_parallel_workers
を増やしてより多くのワーカーが同時に実行できるようにするか、 max_parallel_workers_per_gather
を減らして、プランナがより少ない数のワーカーを要求するようにすることを考慮してください。
与えられたパラレルクエリから起動されたすべてのバックグラウンドワーカープロセスは、そのプランの一部を実行します。
リーダーはそうしたプランの部分を実行するだけでなく、追加の任務が与えられます。
つまり、ワーカーが生成したすべてのタプルを読み込まなければなりません。
プラン中のパラレル部分が少数のタプルしか生成しない場合は、リーダーは追加のワーカーとほぼ同じように振る舞い、クエリの実行を高速化します。
反対にプラン中のパラレル部分が大量のタプルを生成する場合は、リーダーはワーカーが生成したタプルの読み込みと、Gather
ノードあるいはGather Merge
より上位のプランノードが要求する追加の処理ステップに忙殺されるかもしれません。
そのような場合は、リーダーはプランの並列実行部分のごく一部しか処理しません。
プランの並列部分の最上位ノードがGather
ではなくてGather Merge
なら、プランの並列部分を実行する各プロセスはタプルをソート順に生成し、リーダーはソート順を保存するマージを実行していることを意味します。
対照的に、Gather
は、ワーカーから都合の良い順でタプルを読み込むので、ソート順が存在しているとしても、それを壊してしまいます。