重い集計クエリでメモリがパンク?「パーティションワイズ集約」でPostgreSQLを本気にさせる方法
やあ。最近、データ分析基盤のパフォーマンス改善に頭を悩ませている若手エンジニアが増えてきたね。
「数千万件のログデータに対して`GROUP BY`を投げたら、PostgreSQLがディスクスワップを始めてサーバーが悲鳴を上げた」……なんて経験、一度はあるんじゃないかな?
普段何気なく使っている`GROUP BY`だけど、PostgreSQLはデフォルトだと「テーブル全体を読み込んで、メモリ上で巨大なハッシュテーブルを作って集計する」という動きをする。データがメモリに収まればいいけど、溢れた瞬間にクエリは急激に重くなるんだ。
今日はそんな「重い集計」を劇的に軽くする切り札、「パーティションワイズ集約(Partitionwise Aggregate)」について話そうと思う。これを知っているだけで、現場での「クエリの重さ」に対する引き出しがグッと増えるはずだよ。
—
パーティションワイズ集約って何者?
簡単に言うと、「巨大なテーブル全体を一気に集計するんじゃなくて、パーティション単位で先に集計しちゃおうぜ」という最適化手法のことだ。
例えば、`sales`テーブルが「月ごと」にパーティション分割されているとする。普通に集計すると、全データをメモリに乗せて頑張ることになるけど、パーティションワイズ集約が働くと、PostgreSQLはこう動く。
1. 「1月分」のパーティションを読み込み、部分集計する。
2. 「2月分」のパーティションを読み込み、部分集計する。
3. ……最後に、各パーティションの結果をマージする。
これの何が嬉しいかって、メモリ消費量が劇的に減るんだ。一度に扱うデータ量がパーティション単位に限定されるからね。さらに、並列処理(Parallel Query)と組み合わせると、CPUのコアをフル活用して爆速で終わることもある。
実践:設定を確認しよう
実はこれ、PostgreSQL 11以降なら標準でサポートされているんだけど、デフォルトでは「無効」になっていることが多いんだ。まずはここをチェックしよう。
— 現在の設定を確認
SHOW enable_partitionwise_aggregate;
もし `off` になっていたら、まずはセッション単位で試してみるといい。
SET enable_partitionwise_aggregate = on;
具体的な使用例:こんな時に効く!
例えば、売上ログを日付でパーティショニングしているシステムで、月次の集計を出すクエリを投げてみよう。
SELECT
date_trunc(‘month’, sale_date) AS sale_month,
product_id,
SUM(amount)
FROM sales
GROUP BY 1, 2;
このクエリ、もし`sales`テーブルが1億行あったら、普通はヒーヒー言わせながら処理するよね。ここで`EXPLAIN`を叩いてみてほしい。
EXPLAIN SELECT … — 上記クエリ
`enable_partitionwise_aggregate` が有効な時、プランの中に 「Partial Aggregate」 という文字が見えたら成功だ。これは各パーティションで部分的に集計が行われている証拠だよ。
注意点:魔法の杖じゃない
ここまで聞くと「全部ONにすればいいじゃん!」と思うかもしれないけど、ちょっと待って。エンジニアなら「銀の弾丸はない」ことを知っておくべきだ。
- パーティションの数が多すぎるとオーバーヘッドになる: 数千個のパーティションがあるようなテーブルだと、逆に管理コストの方が高くつく場合がある。
- プランナが賢く動かないこともある: 複雑なJOINが絡む場合、統計情報の精度が低いと、かえって効率が落ちることがある。
だから、まずは「実行計画(EXPLAIN ANALYZE)を見て、実際に時間が短縮されているか確認する」という基本を忘れないでほしい。
先輩からのアドバイス
実務でこの機能を使うときは、単に設定をONにするだけじゃなくて、「そもそもその集計が、パーティションキーと合致しているか」を意識してみて。
例えば、`sale_date`で分割しているのに、クエリのWHERE句で関係ない条件ばかり絞り込んでいたら、パーティションワイズ集約の恩恵は受けにくい。テーブルの設計とクエリの書き方がマッチしたとき、初めてデータベースは本領を発揮するんだ。
PostgreSQLは非常に奥が深いデータベースだけど、こういう「仕組み」を理解して味方につけると、本当に頼もしい相棒になってくれる。
また何か詰まったら、いつでも聞きに来てくれ。君の書くコードが、少しでも速く、そして美しくなることを期待しているよ!
コメント