PostgreSQLの並列クエリ、ちゃんと使いこなせてる?「爆速化」の裏にある罠とコツ
どうも。最近、大規模な分析基盤のチューニングで夜通しログと睨めっこしていたら、ふと「そういえば、並列クエリをなんとなくで設定している人が多すぎるな」と思い出したので、今日はこの話をしようと思う。
PostgreSQLの並列クエリ(Parallel Query)は、魔法の杖じゃない。でも、正しく理解して杖を振れば、数分かかっていた重いクエリが数秒で終わることもある。現場で培った「これだけは知っておけ」というポイントをまとめたから、ぜひ最後まで付き合ってほしい。
—
並列クエリの「正体」をイメージしよう
まず大前提として、PostgreSQLの並列クエリは「1つのクエリを複数のバックエンドプロセスで分担して処理する仕組み」だ。
シングルスレッドで一生懸命データをスキャンしていたのを、複数人で手分けして一気に読み込むようなイメージ。当然、CPUのコア数が多ければ多いほど恩恵を受けやすい。でも、ここで勘違いしちゃいけないのが、「並列化=常に速い」ではないってことだ。
プロセスを立ち上げて、結果をマージして…というオーバーヘッド(管理コスト)がかかる。だから、数件のレコードを検索するような軽いクエリで無理に並列化させると、逆に遅くなることだってあるんだ。
—
実践:どうやって並列化をコントロールするか
PostgreSQLの設定ファイル(`postgresql.conf`)にある、このあたりのパラメータをいじったことはあるかな?
- `max_parallel_workers_per_gather`: 1つのクエリで最大何個のプロセスを動かすか。
- `min_parallel_table_scan_size`: 「これ以上のサイズなら並列化を検討するよ」という閾値。
例えば、数百万行あるログテーブルをスキャンするようなバッチ処理なら、以下のような設定が効いてくる。
— テーブルの並列度を直接指定して、クエリを強制的に「呼び込む」こともできる
ALTER TABLE logs SET (parallel_workers = 4);
こうやってテーブル単位で「こいつは重いから並列で頼む」と指示を出しておくと、オプティマイザが判断しやすくなるんだ。
—
「並列化されない!」と嘆く前に見るべきこと
現場でよくあるのが、「設定はしたのに並列化されないんですけど?」という相談。たいてい原因は決まっている。
1. データ量が足りない: 小さなテーブルなら「わざわざ並列化するより一人でやったほうが早いよ」とオプティマイザが判断する。`EXPLAIN` を叩いて、そもそも並列化のコスト見積もりがどうなっているか見てみよう。
2. 関数が「並列不可」になっている: これが一番の落とし穴。自作の関数や一部の標準関数には `PARALLEL SAFE` という属性が必要だ。`PARALLEL UNSAFE` な関数がクエリに含まれていると、問答無用で並列化は無効になる。
— 関数を作るときは必ず属性を確認しよう
CREATE FUNCTION my_heavy_func(int) RETURNS int AS $$
…
$$ LANGUAGE sql PARALLEL SAFE;
—
現場の先輩からのアドバイス:Explainは「嘘をつかない」
結局のところ、並列クエリのチューニングは `EXPLAIN ANALYZE` を叩くことに尽きる。
EXPLAIN ANALYZE SELECT count() FROM large_table WHERE status = ‘processed’;
実行計画に `Parallel Seq Scan` や `Parallel Hash Join` といった文字が見えるか確認してほしい。もし出ていないなら、まずはコスト設定(`parallel_tuple_cost`など)を少しだけ下げて、オプティマイザに「並列化のメリットをもっと高く見積もれ!」と圧をかけてみるのも一つの手だ。
最後に
並列クエリは強力な武器だけど、同時にCPUリソースを大量に食う「諸刃の剣」でもある。Webアプリの同時接続数が多い環境で、全クエリを無理やり並列化しようとすると、CPUが飽和してシステム全体が死ぬこともある。
「このクエリはバッチ用だからリソースを食ってもいい」「これはユーザー体験に関わるからレスポンス優先で」といった具合に、クエリの性格に合わせてチューニングするのが一流のエンジニアだ。
まずは今、開発環境で一番重いクエリを `EXPLAIN` してみるところから始めてみないか?何か詰まったら、また相談してくれ。応援してるよ。
コメント