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)` を見てみてください。どこで諦めたのか、理由のヒントが必ずそこに隠れています。
それでは、良いチューニングライフを!何かあったらまた相談してくださいね。
コメント