【テクニカル・上級編】 制約除外 (Constraint Exclusion) – PostgreSQL

なぜ、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が本来持っている「無駄を省く力」を最大限に引き出すための、最も洗練されたツールの一つです。皆さんのクエリが、今日も無駄なスキャンを避けて軽快に走ることを願っています。

それでは、また次回の深掘りでお会いしましょう。

コメント

タイトルとURLをコピーしました