Article
PostgreSQLの並列スキャン設定:最小サイズがワーカー数にも効く理由
min_parallel_table_scan_size と min_parallel_index_scan_size は、並列スキャンの候補条件だけでなくワーカー数の見積もりにも使われます。PostgreSQL 18の実装をたどり、しきい値・コスト・実行時の空きを切り分けて調整する考え方を整理します。
Share
こはるの読みどころ
ワーカーを増やしたいとき、最小サイズを下げればよいとは限らないんだね。計画された人数と実際に起動した人数を分けて読むのが手掛かりだよ。

PostgreSQLで並列クエリを調整するとき、「もっと小さいテーブルも並列に読めれば速くなるのでは」と考えることがあります。そこで目に入るのが、スキャンの最小サイズを指定する2つの設定です。
Christophe Pettus氏の並列スキャン設定の解説は、この「最小」という名前だけでは見えにくい性質を取り上げています。しきい値を下げると、並列化の対象が広がるだけでなく、要求するワーカー数まで変わり得ます。
では、並列化したいのに動かない場合と、ワーカーを使いすぎる場合をどう見分ければよいのでしょうか。PostgreSQL 18の仕様と実装を使って、サイズの判定から実行時の人数までを順に追います。
並列スキャンの入口は、保存容量より「読む量」で決まる
min_parallel_table_scan_sizeはテーブル側、min_parallel_index_scan_sizeはインデックス側で、並列スキャンを検討する読み取り量の下限を指定します。既定値はそれぞれ8MBと512kBです。PostgreSQL 18の設定仕様で確認できます。
逐次スキャンではテーブル全体が対象ですが、インデックス走査では、条件に応じて触れると見積もったページ数が判断材料になります。大きなインデックスでも、ごく狭い範囲だけを読む検索なら下限に届かないことがあります。
また、単位なしの数値はブロック数です。標準的な8kBブロックの環境では、8は8MBではなく64kBになります。設定を比較するときは、数値と単位をセットで読みたいですね。
ただし、下限を満たすことと、並列プランが採用されることは別です。その間に、ワーカー数の見積もりとコスト比較があります。
最小サイズを下げると、3倍刻みのワーカー数も変わる
通常の単独テーブル走査で、テーブルのparallel_workersを明示していない場合、ワーカー数の見積もりは最小サイズを起点に増えます。PostgreSQL 18.0のcompute_parallel_workerの実装では、起点で1人、読み取り量がその3倍、9倍と増えるたびに1人ずつ加算します。
テーブル側のしきい値が8MBなら、8MB、24MB、72MB、216MBが1人、2人、3人、4人の境界です。たとえば128MBを走査する場合、上限を適用する前の計算は次のようになります。
| テーブル側の最小サイズ | 128MBの走査に対する計算結果 |
|---|---|
| 4MB | 4人 |
| 8MB | 3人 |
| 64MB | 1人 |
| 256MB | サイズ条件で候補から外れる |
これは実装から求めた計算例であり、実測した実行計画ではありません。max_parallel_workers_per_gatherの既定値は2なので、そのままなら表の3人・4人は2人に制限されます。
0を指定しても、この計算自体はなくなりません。起点が最低1ブロックになるため、8kBブロックで128MBなら上限適用前は9人です。小さなクエリを並列化するつもりの変更が、既存の大きな走査の要求人数にも効くわけです。
なお、同じ実装には例外もあります。parallel_workersの明示値はサイズによる人数計算を置き換え、継承・パーティションなどの子リレーションは下限未満でも並列パスを検討できます。表の計算を、そのまま複雑なプラン全体の人数に当てはめることはできません。
インデックス走査では、どちらのしきい値を見るかが異なる
インデックス経由で検索する場合、テーブル本体であるヒープを読むかどうかが効いてきます。通常の並列インデックス走査は両方のサイズ条件を使い、テーブル側とインデックス側で計算したワーカー数の小さい方を採ります。
一方、Index Only Scanのワーカー計算ではヒープ側の判定を省きます。ヒープ読み取りの見積もりが少ないことだけで並列化を排除しないための処理です。この分岐はcost_indexの実装にあります。
Bitmap Heap Scanでは関係が逆になります。インデックスからビットマップを作る処理は1プロセスが行い、その後のヒープ走査を分担するため、並列部分に効くのはテーブル側のしきい値です。PostgreSQL 18で並列Index Scan/Index Only Scanに対応するのはB-treeであることも、並列プランの説明に明記されています。
「インデックスを使っているから、インデックス側の設定を変える」とは限りません。まず実行計画のスキャン方式を特定すると、調整する値を絞れます。
EXPLAINで、サイズ・コスト・起動人数を切り分ける
サイズを満たしても並列プランが出ないなら、コスト比較が次の論点です。parallel_setup_costはワーカー起動、parallel_tuple_costはプロセス間で行を渡す負担をモデル化します。並列プランの調整に関する公式説明でも、コストを下げて選ばれた並列プランが、直列実行より遅い場合があるとしています。
まず、対象セッションの設定を読み取っておきます。
-- 最小サイズと、計画・実行時のワーカー上限を確認する。
SHOW min_parallel_table_scan_size;
SHOW min_parallel_index_scan_size;
SHOW max_parallel_workers_per_gather;
SHOW max_parallel_workers;
SHOW max_worker_processes;
次に、対象SELECTのEXPLAINでGatherやGather MergeとWorkers Plannedを見ます。計画人数は確保済みの人数ではなく、実行時には共有のワーカー枠が不足することもあります。並列クエリの実行モデルを踏まえると、要求が少ない問題と、要求どおり起動できない問題を分けられます。
実行可能な検証環境では、対象SELECTにEXPLAIN (ANALYZE, BUFFERS, VERBOSE)を付け、Workers Launched、実行時間、バッファ使用状況を比較します。ANALYZE付きはクエリを実際に実行するので、表示だけの操作ではありません。
しきい値の効果を比べるなら、同じクエリで他の設定を固定し、候補値を1つずつ試します。トランザクション内のSET LOCALなら変更はそのトランザクションの終了までです。計画人数だけを成功指標にせず、実行時間と、想定する同時実行時のCPU・I/O負荷を判断材料にします。
全体設定を変える前に、インデックス保守への波及を見る
この2つの設定はSELECT専用ではありません。CREATE INDEXの標準的なワーカー数計算もテーブル側のしきい値を使います。plan_create_index_workersの実装では、さらにmax_parallel_maintenance_workersやメモリ量による制約が加わります。
そのため、問い合わせの並列化を抑えるためにテーブル側の最小サイズを大きくすると、インデックス作成の並列化にも影響し得ます。また、VACUUMはインデックス側のしきい値を使いますが、こちらの判断材料は検索条件から推定する走査量ではなく、インデックス自体の大きさです。
並列スキャンの最小サイズは、対象を選ぶ下限であると同時に、要求人数を決める尺度でもあります。まず「サイズで候補から外れた」「コストで採用されなかった」「計画どおり起動できなかった」のどこで期待と違ったのかを見極めると、変えるべき設定が見えてきます。
必要な変更を対象セッションや処理に絞って測ることが、問い合わせと保守処理の両方を意図どおりに動かす近道です。
出典
- Title: Christophe Pettus: All Your GUCs in a Row: min_parallel_index_scan_size and min_parallel_table_scan_size
- URL: https://postgr.es/p/9wW
Share
Related Articles
カテゴリやタグが近い記事を続けて読めるように並べています。




