なぜ、PostgreSQLの「制約除外」をいま改めて語るのか
大規模なデータセットを扱うとき、私たちはしばしば「パーティショニング」という武器を手に取ります。しかし、いざ実装した後に「思ったほどクエリが速くならない」という現実に直面し、頭を抱えた経験はないでしょうか。
PostgreSQLにおける「制約除外(Constraint Exclusion)」は、一見すると枯れた機能のように思えるかもしれません。しかし、実行計画(EXPLAIN)の深淵を覗くと、この機能が単なる「おまけ」ではなく、クエリプランナとストレージエンジンを繋ぐ極めて重要な橋渡し役であることがわかります。
今回は、この制約除外の内部挙動を紐解き、なぜ時にそれが裏目に出るのか、どうすればプランナを「納得」させられるのかについて、少しエンジニア同士の深い話をしましょう。
—
内部アーキテクチャ:プランナはどうやって「見ない」を決めるのか
制約除外の核心は、クエリの `WHERE` 句と、各テーブルに定義された `CHECK` 制約の論理的な「包含関係」にあります。
プランナはクエリをパースした後、対象となるテーブルをスキャンする前に、そのテーブルの `CHECK` 制約をチェックします。もし、`WHERE` 句の条件が制約と矛盾する場合、あるいは「絶対に合致しない」と論理的に証明できる場合、プランナは早々にそのテーブルを「考慮すべき候補」から外します。
ここで重要なのは、これが静的な最適化であるという点です。実行時にデータを読みに行く前に、メタデータ上の論理演算だけで判断を終える。これが制約除外の最大の美点であり、I/Oを極限まで削るための定石です。
なぜ「プランナを納得させる」必要があるのか
プランナは意外と臆病です。型変換が複雑だったり、独自定義の関数が含まれていたりすると、「安全側に倒して」除外を諦めることがあります。
- 型の一致: `timestamp` 型の列に対して `text` 型の値を比較させていませんか? たとえ値が合致していても、型キャストのコストや不透明さがプランナの推論を阻害し、制約除外が効かなくなるケースは後を絶ちません。
- Immutable関数の原則: もし制約に `volatile` な関数が含まれているなら、プランナは「実行のたびに結果が変わるかもしれない」と判断し、静的な除外を避けます。制約には必ず `immutable` な関数を使うのが鉄則です。
—
トラブルシューティング:プランナが「見てしまう」とき
現場で遭遇する「制約除外が効かない!」という悲鳴の多くは、実は非常に単純な理由で発生しています。
1. 継承(Inheritance)の罠
現代のPostgreSQLでは宣言的パーティショニングが主流ですが、古いシステムを運用していると、まだ手動の継承(Inheritance)を使っているケースがあるでしょう。ここで `constraint_exclusion` の設定が `off` になっていると、そもそも機能自体が死んでいます。デフォルトは `partition` ですが、複雑なクエリで効かないと感じたら、一度設定を見直してください。
2. クエリの書き方が引き起こす「推論の放棄」
例えば、以下のようなケースです。
— 良くない例
SELECT FROM measurement WHERE log_date = CURRENT_DATE – INTERVAL ‘1 day’;
`CURRENT_DATE` は実行のタイミングで値が変わる可能性があるため、プランナによっては制約との比較を静的に解決できず、全パーティションをスキャンしようとすることがあります。このような場合は、アプリ側で定数として日付を渡すか、あるいは `STABLE` 関数をうまく活用してプランナにヒントを与える工夫が必要です。
—
現場の視点:最適化のその先へ
制約除外を極めるということは、PostgreSQLの「論理的な思考回路」とシンクロするということです。
私がパフォーマンスチューニングをする際は、必ず `EXPLAIN (COSTS, VERBOSE)` を使い、`Filter` や `Partitions Removed` の行を凝視します。もしスキャン対象から外れるはずのパーティションが残っていれば、それはプランナが「あなたのクエリの意図」を誤解しているサインです。
「なぜプランナはこう判断したのか?」
この問いを繰り返すことで、インデックス設計やパーティションキーの切り方が、物理的なディスク配置以上に「論理的な整合性」に依存していることが見えてくるはずです。
制約除外は魔法ではありません。しかし、PostgreSQLが本来持っている「無駄を省く力」を最大限に引き出すための、最も洗練されたツールの一つです。皆さんのクエリが、今日も無駄なスキャンを避けて軽快に走ることを願っています。
それでは、また次回の深掘りでお会いしましょう。
コメント