パーティションプルーニングを飼い慣らす:クエリ実行の「無駄」を削ぎ落とす技術
PostgreSQLのパーティショニングを単なる「巨大テーブルの分割」として捉えているなら、それは少しもったいない。パフォーマンスの真髄は、実は「何を読まないか」にあります。そう、パーティションプルーニング(Partition Pruning)の話です。
今回は、この機能がいかにしてクエリの爆速化を支えているのか、その内部構造と、現場でよく遭遇する「なぜか効かない」トラブルの解剖学について、少し踏み込んで語りたいと思います。
—
1. 静的プルーニング:プランナの先読み能力
PostgreSQLが実行計画を作成する段階(Planning Phase)で、検索条件とパーティション定義を照らし合わせ、不要なパーティションを排除する。これが「静的プルーニング」です。
例えば、`created_at` でレンジパーティションを張っている場合、`WHERE created_at = ‘2023-10-01’` といった定数指定があれば、オプティマイザは瞬時に「必要なのはこのパーティションだけだ」と判断します。
ここでエンジニアが意識すべきは、「定数として評価できるか」という点です。
複雑な関数や、不安定なユーザー定義関数をWHERE句に混ぜてしまうと、プランナは「実行してみるまで境界がわからない」と判断し、静的プルーニングを諦めてしまうことがあります。プランナに優しくあるためには、条件式をできるだけ「計算可能な定数」に近づけることが鉄則です。
2. 実行時プルーニング:プランナを出し抜く動的最適化
PostgreSQL 11以降、大きく進化したのが「実行時プルーニング(Run-time Pruning)」です。
静的なプランニング時には値が確定せずとも、クエリ実行の直前や実行中に値が判明した場合、そのタイミングで不要なパーティションを切り捨てる仕組みです。特にJOIN操作やサブクエリが絡む場合に威力を発揮します。
例えば、マスタテーブルとパーティションテーブルを結合する際、マスタ側の絞り込み結果を使ってパーティションをプルーニングするケースですね。
内部的には、`PlannedStmt` が保持するパーティションリストに対して、実行時に `exec_partition_prune_steps` が評価されます。ここで重要なのは、「プルーニングにかかるコスト」と「スキャンを省略するメリット」のバランスです。極端にパーティション数が数千を超えると、この評価コスト自体がボトルネックになることもあります。
3. 「なぜ効かない?」現場のトラブルシューティング
パーティションを正しく設計したはずなのに、EXPLAINを見ると「全パーティションをスキャンしている」……。そんな絶望的な状況に陥ったとき、私がまずチェックするのは以下のポイントです。
- データ型の不一致と暗黙のキャスト
パーティションキーが `timestamptz` なのに、クエリで `text` 型を渡していませんか? 暗黙のキャストが発生すると、プランナは「型が違うから評価できない」と判断し、プルーニングを放棄することがあります。`EXPLAIN` の出力に `Filter` 句が出ていないか、`Partitions Removed` が `0` になっていないかを確認してください。
- プランナの限界を超えた式
`WHERE created_at > (SELECT max(date) FROM other_table)` のような複雑な相関サブクエリは、実行時プルーニングを誘発しにくい場合があります。時には、アプリ側で境界値を算出して定数としてクエリに渡すほうが、遥かに健全な実行計画を得られることがあります。
- パーティションキーの「断片化」
もし論理的なキーと物理的なパーティションキーが一致していない場合、どんなに優れたプルーニング機能も無力です。データモデルを設計する際は、クエリのパターンとパーティションキーが数学的に一致しているか、もう一度見直してみてください。
4. 最後に:インデックスとの共存
パーティションプルーニングが機能すれば、インデックススキャンの対象範囲も物理的に小さくなります。これはI/O負荷の劇的な軽減を意味します。
私の経験上、最もパフォーマンスが出るのは「プルーニングでパーティションを1つに絞り込み、その上で適切なインデックスを使ってピンポイントでレコードを射抜く」というパターンです。
もし今、あなたのPostgreSQLが重いと感じているなら、まずは `EXPLAIN (ANALYZE, BUFFERS)` を叩いてみてください。`Subplans Removed` や `Partitions Removed` の項目を凝視するだけで、DBがどこで迷い、どこで無駄なスキャンを行っているのか、その「本音」が見えてくるはずです。
データベースは嘘をつきません。ただ、エンジニアの問いかけを待っているだけなのです。
—
それでは、良いクエリライフを。次回は、パーティションキーにおける「局所性」とパフォーマンスの相関についてもう少し深く潜ってみようと思います。
コメント