【実務・中級編】 並列クエリ実行(Parallel Query) – PostgreSQL

PostgreSQLの並列クエリ、まずは「魔法の杖ではない」と知ることから始めよう

「最近、PostgreSQLのクエリが重いんだよね……。よし、`max_parallel_workers_per_gather` を増やして爆速にしてやるか!」

……ちょっと待って。その気持ち、痛いほどよくわかります。でも、現場で何度も見てきた光景なんです。設定をいじって逆にシステム全体が重くなり、慌てて戻す羽目になるエンジニアを。

PostgreSQLの「並列クエリ(Parallel Query)」は確かに強力な武器です。でも、これを使いこなすには、単なるパラメータ調整以上の「仕組みへの理解」が不可欠なんですよ。今日は、現場で血を流しながら学んだ、並列クエリの正しい付き合い方についてお話しします。

—

並列クエリは「人海戦術」だと思えばいい

PostgreSQLの並列クエリは、簡単に言えば「1つのタスクを複数のワーカープロセスで分担して処理する」仕組みです。

例えば、数百万行のテーブルをフルスキャンして集計するような処理を想像してください。これを1人でやれば日が暮れますよね。でも、4人のワーカーが手分けしてデータを読み込み、最後にリーダープロセスが結果をまとめたら? 当然、時間は短縮されます。

このとき、PostgreSQLは `Gather` ノードというものを使って、バラバラに処理された結果を合体させます。実行計画(`EXPLAIN`)を見たときに `Parallel Seq Scan` や `Gather` という文字を見かけたら、それは「お、頑張って並列化してるな」というサインです。

—

実践:並列化が発動するための「3つの扉」

並列クエリが動くためには、いくつかの条件をクリアしなければなりません。よくある「並列化してくれない!」という悩みは、大抵このどれかに引っかかっています。

1. データ量(コスト)の壁:
PostgreSQLは「並列化するオーバーヘッド」を計算しています。`min_parallel_table_scan_size` より小さいテーブルなら、わざわざワーカーを立ち上げるより1人でやったほうが速いと判断され、並列化されません。
2. 設定値の制約:
`max_parallel_workers_per_gather` が設定されていても、システム全体の `max_parallel_workers` の上限を超えれば動きません。
3. クエリの内容:
残念ながら、すべてのクエリが並列化できるわけではありません。例えば、`LEAKPROOF` 属性のない関数を呼んでいたり、カーソルを使っていたりすると、安全のために並列化を諦めることが多いです。

—

具体的な設定と確認のステップ

まずは、今のクエリがどう動いているかを確認しましょう。

— まずは実行計画を見てみる
EXPLAIN ANALYZE
SELECT count(), category_id
FROM heavy_logs
GROUP BY category_id;

もしここで `Parallel` の文字が見えないなら、まずはコストを確認します。

— 一時的に設定を緩めてみる(セッション単位で!)
SET max_parallel_workers_per_gather = 4;
SET min_parallel_table_scan_size = ‘1MB’; — あえて小さくして強制的に試す

もしこれで並列化されるようになったら、テーブルの統計情報(`ANALYZE`)が古いだけかもしれません。統計情報が古いと、PostgreSQLは「このテーブルは小さい」と勘違いして、並列化のメリットがないと判断してしまいます。まずは `ANALYZE` を打つ。これが現場の鉄則です。

—

注意点:並列化の「副作用」を忘れるな

ここからが先輩として一番伝えたいこと。並列クエリは、CPUとメモリのリソースを激しく奪い合います。

  • メモリ食い: `work_mem` はワーカーごとに消費されます。もし `work_mem` を大きく設定したまま並列度を上げると、サーバーのメモリが一瞬で枯渇し、OOM Killerにプロセスを殺される……なんて悲劇が待っています。
  • I/O飽和: ディスクがSSDならまだしも、HDD構成だと並列読み込みがかえってI/O待ちを悪化させることもあります。
  • OLTPには不向き: 同時接続数が多いWebアプリでこれをやると、一人の重いクエリがワーカーを独占し、他のユーザーのレスポンスが壊滅的になります。

—

まとめ:賢く付き合うために

並列クエリは、「集計処理やバッチ処理」といった、重い読み取りクエリを高速化する最高の手法です。しかし、「同時接続が多いOLTP(オンライン・トランザクション処理)」で安易に使うのは禁物です。

僕からのアドバイスは一つ。
「まずはインデックスと実行計画を見直すこと。その上で、どうにもならない巨大な分析クエリに対してのみ、慎重に並列化の閾値を調整すること」。

魔法の杖を探す前に、まずは足元のクエリを磨く。これが、結局一番の近道だったりするんですよ。

もし「設定を変えてもどうしても並列化されない!」と悩んだら、`EXPLAIN (VERBOSE, BUFFERS)` を見てみてください。どこで諦めたのか、理由のヒントが必ずそこに隠れています。

それでは、良いチューニングライフを!何かあったらまた相談してくださいね。

コメント

タイトルとURLをコピーしました