PostgreSQLの心臓部、クエリリライタの深淵に触れる
PostgreSQLのアーキテクチャを語る際、オプティマイザやMVCCの議論は華やかですが、その手前でひっそりと、しかし極めて重要な役割を果たしているのが「クエリリライタ(Query Rewriter)」です。
正直なところ、多くのエンジニアが「SQLを書けば、プランナが最適化して実行してくれる」というブラックボックス的な認識で運用していることでしょう。しかし、PostgreSQLを極めようとするなら、SQLがパーサを通過した直後、`Query`ツリーがいかにして変容させられているのかを知っておく必要があります。
今日は、この「クエリリライタ」の内部構造と、それが時として引き起こす罠について、少し深掘りしてみましょう。
—
クエリリライタは何をしているのか?
クエリリライタは、パーサが生成した「生のクエリツリー」を受け取り、`pg_rewrite`システムカタログに格納されたルールに基づいて、それを「より実行に適した形」に書き換えるコンポーネントです。
特筆すべきは、単なる構文変換ではない点です。リライタは「ルールシステム(Rule System)」の心臓部であり、以下の処理を担います。
- ビュー(View)の展開: 複雑なビュー参照を、元のテーブルに対するクエリへインライン展開します。
- 条件付きルール(ON SELECT/INSERT/UPDATE/DELETE)の適用: `CREATE RULE`で定義された、あの少し癖のある(そして時に悪名高い)ルールをクエリツリーに統合します。
- システム列の解決: 継承テーブルに対する操作などの最適化。
なぜ「ルール」は奥が深いのか
「ルールシステムは使わずにトリガーを使え」という格言を耳にしたことはありませんか? これは現場のエンジニアにとって、ある種の教訓です。
リライタがクエリを書き換えるということは、「あなたの意図したSQLが、リライタを通った瞬間に全く別の実行計画を背負うことになる」ことを意味します。
特に、`INSTEAD`ルールを用いた場合、元のクエリは破棄され、ルール定義内のクエリに差し替わります。これが大規模なクエリになると、デバッグは地獄絵図です。`EXPLAIN`で表示されるクエリが、あなたが書いたSQLとは似ても似つかない巨大なジョインツリーに膨れ上がっているのを見たことはありませんか? それこそが、リライタが「親切心」で展開した結果なのです。
パフォーマンストラブルシューティングの勘所
リライタに関連するトラブルで最も多いのは、「クエリが異常に肥大化し、オプティマイザがギブアップする」というケースです。
1. ビューのネストとクエリの爆発
深い階層のビューを重ねると、リライタはそれらを再帰的に展開します。この際、中間結果のサブクエリが非常に複雑になり、プランナが統計情報をうまく活用できなくなることがあります。
- 対策: `EXPLAIN`の結果をじっくり眺めてください。リライタが展開した後のツリーが複雑すぎる場合は、ビューを物理的なマテリアライズドビューに置き換えるか、CTEを使って実行順序を強制的に制御することを検討すべきです。
2. 条件付きルールの意図せぬ副作用
`ON INSERT DO INSTEAD`のようなルールを多用すると、挿入したはずのデータが別のテーブルに飛んでいたり、期待しないロックを取得していたりすることがあります。
- 対策: `pg_rewrite`の内容を定期的に監査し、「本当にこのルールが必要か?」を自問自答してください。多くの場合、`BEFORE/AFTER`トリガーで解決できるはずです。トリガーはプランナが計画を立てる際、ある種の「境界」として振る舞うため、リライタによる意図しないクエリの肥大化を抑えやすいというメリットがあります。
最後に:ブラックボックスを愛するために
PostgreSQLの柔軟性は、このクエリリライタの存在によって支えられています。もしリライタがなければ、私たちは複雑なビューを扱うたびにアプリケーション側で膨大なSQLを生成しなければならなかったでしょう。
リライタは、魔法のような便利さを提供する一方で、その内部構造を理解していないエンジニアを裏切る側面も持っています。「なぜこのクエリは遅いのか?」と悩んだとき、プランナの統計情報ばかりを見るのではなく、「そもそも、リライタはこのクエリをどう書き換えているのか?」という視点を持ってみてください。
`EXPLAIN`の出力結果を眺めながら、その裏側にあるリライタの「仕事」を想像できるようになれば、あなたも立派なPostgreSQLの熟練者です。
技術の深淵を覗くことは、時に恐ろしいものですが、その先には必ず、今まで見えなかった最適化の景色が広がっています。それでは、また次回の深掘りでお会いしましょう。
コメント