TECH BLOG
技術ブログ
  • 2026-01-30 PostgreSQLPostgreSQL バージョンアップPostgreSQL パラメータ

    PostgreSQL18 で pg_stat_statements に追加されたパラレルクエリ用のワーカープロセスの累積値

概要

PostgreSQL では、1つのクエリを複数のプロセスで同時に処理する「パラレルクエリ」という仕組みが用意されています。
パラレルクエリは PostgreSQL 9.6 で導入され、バージョンアップに伴い機能向上が行われています。

PostgreSQL 18 でのパラレルクエリ関連の機能向上には、パラレルクエリ実行時の起動予定ワーカー数と、実際に起動したワーカー数が記録されるようになったことが該当します。
今回は、その追加された機能についての説明記事となります。

なお、パラレルクエリの詳細については公式ドキュメントに詳しく記載されていますので、そちらも合わせて参照してください。

【 PostgreSQL 17.6文書 - 第15章 パラレルクエリ - 】
https://www.postgresql.jp/document/17/html/parallel-query.html

PostgreSQL 18 で追加された情報

PostgreSQL 18 で、pg_stat_statements ビューの parallel_workers_to_launch とparallel_workers_launched が追加されました。
この二つの情報からは、パラレルクエリのためのワーカープロセスの起動状況の履歴を確認できます。

パラメータ名 役割
parallel_workers_to_launch クエリ実行時に起動予定のパラレルクエリ用のワーカープロセス数の累積
parallel_workers_launched クエリ実行時に実際に起動されたパラレルクエリ用のワーカープロセス数の累積

pg_stat_statments についても、公式ドキュメントを参照してください(現時点では、v18 用の日本語ドキュメントは公開されていないため、翻訳作業中の URL を記載しています)。

【 PostgreSQL 18.0文書 - F.30. pg_stat_statements — SQL文のプラン生成時と実行時の統計情報を記録する - 】
https://pgsql-jp.github.io/current/html/pgstatstatements.html

パラレルクエリ用のワーカープロセスの起動のしくみ

parallel_workers_to_launch と parallel_workers_launched の説明の前に、パラレルクエリ用のワーカープロセスの起動のしくみについて、説明をします。

パラレルクエリが実行される際に、まずプランナが、いくつのワーカープロセスを起動するかを計画します。このときの計画した数が、parallel_workers_to_lanch に加算されます。
その後、実際にクエリを実行する際に、計画した数のワーカープロセスを起動して、パラレル処理が実行されます。このときの起動された数が、parallel_workers_lanched に加算されます。

また、パラレルクエリ用のワーカープロセスは無制限に作成できるわけではなく、パラメータによって上限が制御されます。
具体的には、max_worker_processes、max_parallel_workers およびmax_parallel_workers_per_gather の範囲内で起動できる仕様となっています。

max_worker_processes

max_worker_processes は、サーバ全体で同時に起動できるワーカープロセス(パラレルクエリ用のワーカープロセス、バックグラウンドワーカープロセス、論理レプリケーション用のワーカープロセスなど)の、総数の上限となるパラメータです。

max_parallel_workers

max_parallel_workers は、サーバ全体で同時に起動できるパラレルクエリ用のワーカープロセスの総数の上限となるパラメータです。この上限は、すべてのセッションで共有されます。

max_parallel_workers_per_gather

max_parallel_workers_per_gather は、1つのクエリにおける「Gather」または「Gather Merge」ノード(※)ごとに起動できる、パラレルクエリ用のワーカープロセス数の上限をとなるパラメータです。この上限は、セッション単位での設定となります。
※「Gather」および「Gather Merge」ノードは、パラレルクエリにおいて、複数のワーカープロセスが分担して処理した結果を1か所に集めるための処理を行います。

起動できるパラレルクエリ用のワーカープロセス数

つまり、パラレルクエリ用のワーカープロセスは、max_worker_processes や max_parallel_workers といったサーバ全体の上限から、現在使用中のワーカープロセス数を差し引いた残数(空いている分)しか起動できません。
例えば、max_parallel_workers = 16 の環境で、他のセッションがすでに 12 個のワーカープロセスを使用している場合、新たに実行されるクエリでは、最大でも 4 個までしか追加で起動できません。

実際に検証

前述のとおり、parallel_workers_to_lanch には計画されたパラレルクエリ用のワーカープロセスの起動数が加算され、parallel_workers_lanched には実際に起動されたパラレルクエリ用のワーカープロセス数が加算されます。
この挙動を実際に PostgreSQL を動作させて確認します。

環境

検証に利用した環境は以下となります。

  • Ubuntu 24.04
  • PostgreSQL 18.1
検証に利用するSQL

検証には pgbench のテーブルを利用しました。
実際に実行した SQL は以下のようなものです。

SELECT
  bid, SUM(abalance)
FROM
  pgbench_branches pb 
    INNER JOIN pgbench_accounts pa USING (bid) 
GROUP BY bid;
実行計画の確認

この SQL の実行計画を下記に記載します。

                                                                             QUERY PLAN                                                                              
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize GroupAggregate  (cost=25202.81..25205.83 rows=10 width=12) (actual time=84.620..85.641 rows=10.00 loops=1)
   Group Key: pb.bid
   Buffers: shared hit=2733 read=13678
   ->  Gather Merge  (cost=25202.81..25205.61 rows=24 width=12) (actual time=84.616..85.636 rows=30.00 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=2733 read=13678
         ->  Sort  (cost=24202.79..24202.81 rows=10 width=12) (actual time=83.193..83.195 rows=10.00 loops=3)
               Sort Key: pb.bid
               Sort Method: quicksort  Memory: 25kB
               Buffers: shared hit=2733 read=13678
               Worker 0:  Sort Method: quicksort  Memory: 25kB
               Worker 1:  Sort Method: quicksort  Memory: 25kB
               ->  Partial HashAggregate  (cost=24202.52..24202.62 rows=10 width=12) (actual time=83.172..83.178 rows=10.00 loops=3)
                     Group Key: pb.bid
                     Batches: 1  Memory Usage: 32kB
                     Buffers: shared hit=2719 read=13678
                     Worker 0:  Batches: 1  Memory Usage: 32kB
                     Worker 1:  Batches: 1  Memory Usage: 32kB
                     ->  Hash Join  (cost=1.23..22119.19 rows=416667 width=8) (actual time=0.161..55.927 rows=333333.33 loops=3)
                           Hash Cond: (pa.bid = pb.bid)
                           Buffers: shared hit=2719 read=13678
                           ->  Parallel Seq Scan on pgbench_accounts pa  (cost=0.00..20560.67 rows=416667 width=8) (actual time=0.142..18.331 rows=333333.33 loops=3)
                                 Buffers: shared hit=2716 read=13678
                           ->  Hash  (cost=1.10..1.10 rows=10 width=4) (actual time=0.013..0.014 rows=10.00 loops=3)
                                 Buckets: 1024  Batches: 1  Memory Usage: 9kB
                                 Buffers: shared hit=3
                                 ->  Seq Scan on pgbench_branches pb  (cost=0.00..1.10 rows=10 width=4) (actual time=0.005..0.006 rows=10.00 loops=3)
                                       Buffers: shared hit=3
 Planning:
   Buffers: shared hit=114
 Planning Time: 0.256 ms
 Execution Time: 85.698 ms
(33 rows)

パラレルクエリが行われている部分を下記に抜粋します。

-> Gather Merge (cost=25202.81..25205.61 rows=24 width=12) (actual time=84.616..85.636 rows=30.00 loops=1)
Workers Planned: 2
Workers Launched: 2

Wokers Planned が、計画時のパラレルクエリ用のワーカープロセスの起動数です。
Wokers Launched が、実行時のパラレルクエリ用のワーカープロセスの起動数です。

パラレルクエリ用のワーカープロセスが計画通りに起動できなかった場合の実行計画も記載します。

                                                                              QUERY PLAN                                                                               
-----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize GroupAggregate  (cost=25202.81..25205.83 rows=10 width=12) (actual time=611.575..612.638 rows=10.00 loops=1)
   Group Key: pb.bid
   Buffers: shared hit=6447 read=9889
   ->  Gather Merge  (cost=25202.81..25205.61 rows=24 width=12) (actual time=611.572..612.633 rows=20.00 loops=1)
         Workers Planned: 2
         Workers Launched: 1
         Buffers: shared hit=6447 read=9889
         ->  Sort  (cost=24202.79..24202.81 rows=10 width=12) (actual time=608.447..608.449 rows=10.00 loops=2)
               Sort Key: pb.bid
               Sort Method: quicksort  Memory: 25kB
               Buffers: shared hit=6447 read=9889
               Worker 0:  Sort Method: quicksort  Memory: 25kB
               ->  Partial HashAggregate  (cost=24202.52..24202.62 rows=10 width=12) (actual time=608.437..608.440 rows=10.00 loops=2)
                     Group Key: pb.bid
                     Batches: 1  Memory Usage: 32kB
                     Buffers: shared hit=6440 read=9889
                     Worker 0:  Batches: 1  Memory Usage: 32kB
                     ->  Hash Join  (cost=1.23..22119.19 rows=416667 width=8) (actual time=0.071..475.393 rows=500000.00 loops=2)
                           Hash Cond: (pa.bid = pb.bid)
                           Buffers: shared hit=6440 read=9889
                           ->  Parallel Seq Scan on pgbench_accounts pa  (cost=0.00..20560.67 rows=416667 width=8) (actual time=0.056..307.786 rows=500000.00 loops=2)
                                 Buffers: shared hit=6438 read=9889
                           ->  Hash  (cost=1.10..1.10 rows=10 width=4) (actual time=0.008..0.008 rows=10.00 loops=2)
                                 Buckets: 1024  Batches: 1  Memory Usage: 9kB
                                 Buffers: shared hit=2
                                 ->  Seq Scan on pgbench_branches pb  (cost=0.00..1.10 rows=10 width=4) (actual time=0.004..0.005 rows=10.00 loops=2)
                                       Buffers: shared hit=2
 Planning:
   Buffers: shared hit=114
 Planning Time: 0.173 ms
 Execution Time: 612.685 ms
(31 rows)

Wokers Planned が 2 ですが、Wokers Launched は 1 となっています。これは SQL 実行時にワーカープロセスを起動するための空きが 1 しかなかったことを表しています。

pg_stat_statements の確認

次に pg_stat_statements を確認します。
pg_stat_statements は、SQL ごとに情報を保持しますので、実行される SQL の種類数が pg_stat_statements のレコード数になります。

postgres=# SELECT query, calls, parallel_workers_to_launch, parallel_workers_launched FROM pg_stat_statements
                                                              query                                                              | calls | parallel_workers_to_launch | parallel_workers_launched 
---------------------------------------------------------------------------------------------------------------------------------+-------+----------------------------+---------------------------
 SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid                            |     3 |                          6 |                         4
 SELECT pg_stat_statements_reset()                                                                                               |     1 |                          0 |                         0
 SELECT query, calls, parallel_workers_to_launch, parallel_workers_launched FROM pg_stat_statements                              |     4 |                          0 |                         0
 EXPLAIN (ANALYZE, BUFFERS) SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid |     5 |                          0 |                         0
 VACUUM ANALYZE                                                                                                                  |     1 |                          0 |                         0
(5 rows)

上記の通り、1 行目の SQL に関して parallel_workers_to_launch が 6 で、parallel_workers_launched が 4 であることを確認できます。
加算される挙動も確認しておきたいので、もう一度、検証用の SQL を実行します。

postgres=# SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid;
 bid | sum 
-----+-----
   1 |   0
   2 |   0
   3 |   0
   4 |   0
   5 |   0
   6 |   0
   7 |   0
   8 |   0
   9 |   0
  10 |   0
(10 rows)

postgres=# SELECT query, calls, parallel_workers_to_launch, parallel_workers_launched FROM pg_stat_statements WHERE queryid = 705003968966668897;
                                                query                                                 | calls | parallel_workers_to_launch | parallel_workers_launched 
------------------------------------------------------------------------------------------------------+-------+----------------------------+---------------------------
 SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid |     4 |                         8 |                         6
(1 row)

想定通りに parallel_workers_to_launch と parallel_workers_launched に 2 が加算され、parallel_workers_to_launch は 6 から 8 に、parallel_workers_launched は 4 から 6 になりました。

EXPLAIN ANALYZE では加算されない

注意点として、EXPLAIN ANALYZE を先頭につけて SQL 実行した場合には、parallel_workers_to_launch と parallel_workers_launched に加算されません。
実際に SQL を実行して挙動を確認します。

postgres=# EXPLAIN ANALYZE SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid;
                                                                              QUERY PLAN                                                                              
----------------------------------------------------------------------------------------------------------------------------------------------------------------------
 Finalize GroupAggregate  (cost=25202.81..25205.83 rows=10 width=12) (actual time=122.134..122.893 rows=10.00 loops=1)
   Group Key: pb.bid
   Buffers: shared hit=299 read=16112
   ->  Gather Merge  (cost=25202.81..25205.61 rows=24 width=12) (actual time=122.118..122.874 rows=30.00 loops=1)
         Workers Planned: 2
         Workers Launched: 2
         Buffers: shared hit=299 read=16112
         ->  Sort  (cost=24202.79..24202.81 rows=10 width=12) (actual time=108.195..108.197 rows=10.00 loops=3)
:
(略)

postgres=# SELECT query, calls, parallel_workers_to_launch, parallel_workers_launched FROM pg_stat_statements WHERE queryid =  -4181577892402298305;
                                                        query                                                         | calls | parallel_workers_to_launch | parallel_workers_launched 
----------------------------------------------------------------------------------------------------------------------+-------+----------------------------+---------------------------
 EXPLAIN ANALYZE SELECT bid, SUM(abalance) FROM pgbench_branches pb JOIN pgbench_accounts pa USING (bid) GROUP BY bid |     1 |                          0 |                         0
(1 row)

上記の通り、実行計画上は

Workers Planned: 2
Workers Launched: 2

となっているのですが、parallel_workers_to_launch および parallel_workers_launched は 0 となっています。
これは、pg_stat_statements 上では、EXPLAIN ANALYZE のありなしで別の SQL として情報が集計されることが理由です。
そのため、実際に parallel_workers_to_launch および parallel_workers_launched の累計値を確認する際には、EXPLAIN ANALYZE なしの情報を確認してください。

まとめ

以上が PostgreSQL18 で pg_stat_statements に追加されたパラレルクエリ用のワーカープロセスの累積値の説明となります。
パラレルクエリは、SQL の高速化を実現する上で重要な機能の一つです。
本番運用されている環境において、今回説明した pg_stat_statements の情報から、想定通りにパラレルクエリが実行されているかを、定期的に確認することが推奨されます。
次回以降の記事では、パラレルクエリ関連パラメータの調整方針についても取り上げていく予定です。

CATEGORY

ARCHIVE

PostgreSQLに関するご相談は
株式会社インサイトまで
お気軽にお問い合わせください。

CONTACT