パーティションワイズ結合で、巨大テーブルのクエリを「劇的」に軽くする話
やあ。最近、PostgreSQLのパフォーマンスチューニングで悩んでる後輩くんから、「巨大なテーブル同士の結合がどうしても遅くて、メモリも食いまくっちゃうんです…」って相談を受けたんだ。
わかるよ、その気持ち。数千万行、あるいは数億行規模のテーブルを結合するとき、PostgreSQLが「ハッシュ結合(Hash Join)のためにメモリが足りないから、ディスクに退避(Disk Spill)させますね」なんて言い出した時の絶望感と言ったら……ね。
今日は、そんな泥沼から救い出してくれる必殺技、「パーティションワイズ結合(Partition-wise Join)」について話そうと思う。これを知っているか知らないかで、現場のエンジニアとしての引き出しの深さがガラッと変わるよ。
—
なぜ、普通の結合は「重い」のか?
まず前提として、PostgreSQLが通常行う結合処理を思い出してみてほしい。
2つの巨大テーブルを結合するとき、PostgreSQLは全体をスキャンして、ハッシュテーブルを構築して……という手順を踏むよね。もしテーブルがメモリに収まらないサイズなら、処理の一部をディスクに書き出す。この「ディスクI/O」が発生した瞬間に、クエリのレスポンスは目に見えて悪化する。
ここで考えたいのが、「もし、結合したい両方のテーブルが、同じキーでパーティショニングされていたら?」という点だ。
例えば、「売上データ」と「売上詳細」を、両方とも`created_at`(月単位)でパーティショニングしているとする。このとき、1月のデータは1月のパーティション同士、2月は2月同士で結合すれば結果は同じだよね。
これが「パーティションワイズ結合」の考え方だ。巨大な全体を一度に処理するのではなく、小さなパーティション単位で「各個撃破」していくわけだ。
—
実践:パーティションワイズ結合を有効にする
この機能、実はPostgreSQL 11以降でかなり賢くなったんだけど、デフォルトではOFFになっていることが多い。まずは設定を確認しよう。
— 設定確認
SHOW enable_partitionwise_join;
これが `off` になっているなら、まずはセッション単位でもいいから `ON` にしてみよう。
— 有効化
SET enable_partitionwise_join = on;
これだけで、PostgreSQLのオプティマイザは「あ、これパーティション同士で結合できるじゃん!」と判断し、実行計画(EXPLAIN)がガラッと変わるはずだ。
—
具体的な使用例と注意点
例えば、こんなケースを想像してみてほしい。
SELECT
o.id, d.item_name
FROM orders o
JOIN order_details d ON o.id = d.order_id
WHERE o.created_at >= ‘2023-01-01’ AND o.created_at < '2023-02-01';
このとき、`orders` と `order_details` の両方が `created_at` でパーティショニングされていれば、最適化が効きやすい。
ただし、ここが重要だ。
パーティションワイズ結合が効くためには、以下の条件が揃っている必要がある。
1. パーティションキーが同じであること:これが大前提。
2. 結合条件にパーティションキーが含まれていること:これがないと、オプティマイザが「どっちのパーティション同士を結合すればいいか」を判断できない。
3. パーティションの構成が一致していること:できれば、パーティションの境界値も揃えておくのがベストだ。
—
「魔法」じゃない。魔法使いになろう
「じゃあ、全部のクエリでこの設定をONにすればいいんですね!」と言うと、ちょっと待ったと言いたい。
実は、この機能は「オプティマイザの計算コスト」を増大させるという側面がある。結合するパーティションの数が増えれば増えるほど、プラン作成の段階で「どの組み合わせが良いか」を検討するパターンが爆発的に増えるからだ。
だから、僕のアドバイスとしてはこうだ。
- 闇雲に全体設定をONにしない: まずは特定の重いバッチ処理や、複雑な集計クエリの実行計画を確認して、効果がありそうな箇所で `SET` してみる。
- EXPLAIN ANALYZE を必ず見る: 実際に `Nested Loop` や `Hash Join` が各パーティション単位で行われているかを確認する。「Partition-wise Join」という文字が実行計画に出てきたら、ニヤリとしていい瞬間だ。
- データ構造を見直す: そもそもパーティションワイズ結合が必要なほど巨大なクエリを頻繁に投げているなら、データモデル自体に無理がないか、あるいはインデックス設計で解決できないかをもう一度考えてみてほしい。
—
最後に
データベースエンジニアの仕事って、結局のところ「いかに計算量を減らし、いかにディスクI/Oを抑えるか」というパズルだと思うんだ。
パーティションワイズ結合は、そのパズルを解く強力な武器になる。特に数百万行を超えるテーブルを扱うような現場では、これが「クエリがタイムアウトする」と「数秒で終わる」の分かれ目になることもある。
まずは開発環境で、自分の手元のクエリがどう変化するか試してみてほしい。もし詰まったら、いつでも聞きに来てくれよ。現場からは以上だ!
コメント