データベースの「心臓部」を覗く:`pg_proc` とその深淵なる世界
PostgreSQLを長年触っていると、ふと「この関数の裏側はどうなっているんだ?」という根源的な疑問に突き当たることがあります。パフォーマンスチューニングで実行計画を眺めているとき、あるいは複雑な拡張機能を自作しているとき。そんな時、我々エンジニアが必ず辿り着く場所がシステムカタログ、中でも `pg_proc` です。
単なる「関数のリスト」だと思ったら大間違いです。ここは、PostgreSQLの実行エンジンがクエリをどう解釈し、どう実行するかを決定づける、いわば「知能の設計図」が格納されている場所なのです。
pg_proc が保持する「実行時のメタデータ」
`pg_proc` を単純な `SELECT ` で眺めるのは、辞書をアルファベット順に読むようなものです。ここには、PostgreSQLのオプティマイザが判断を下すための、極めて重要なメタデータが詰まっています。
特に注目すべきは以下のカラムたちです。
- `provolatile`: これが曲者です。`v` (volatile), `s` (stable), `i` (immutable) の指定は、オプティマイザの最適化戦略を劇的に変えます。例えば、`immutable` と誤認させている関数が実は重い外部参照を行っていた場合、オプティマイザはクエリの評価を省略し、結果をキャッシュしようとして思わぬ落とし穴にハマります。
- `procost`: 実行コストの推定値です。カスタム関数を書く際、ここを適切に設定しないと、プランナは「この関数は軽い」と判断し、本来なら避けるべき並列実行計画を立ててしまい、結果としてCPUを枯渇させる原因になります。
- `proparallel`: PostgreSQL 9.6以降、並列クエリが導入されてから重要度が増しました。`safe` なのか `restricted` なのか。ここを正しく定義しないと、せっかくの並列化が封印されてしまいます。
パフォーマンストラブルシューティングの最前線として
現場で「なぜかこの関数が遅い」「なぜこのクエリだけプランが悪いのか」というトラブルに遭遇したとき、私はまず `pg_proc` を確認します。
例えば、関数の引数型(`proargtypes`)と実際の入力値の型にわずかなズレがあり、暗黙的な型変換(Cast)が発生していないか?あるいは、`proisstrict` が `false` になっているせいで、NULL値に対して無駄な関数呼び出しが走っていないか?
こうした低レイヤーの事実は、`EXPLAIN ANALYZE` の出力だけでは見えてこないことが多々あります。`pg_proc` を覗くことで、「データベースがその関数をどのように見ているか」という視点を得る。これが、熟練エンジニアとそうでないエンジニアの境界線だと言っても過言ではありません。
システムカタログを扱うときの「作法」
もちろん、`pg_proc` を直接 `UPDATE` するような蛮行は推奨しません。我々がやるべきは、`CREATE FUNCTION` 文の `WITH` 句やコスト定義を徹底的に磨き上げることです。
「たかが関数、されど関数」。
PostgreSQLの柔軟性は、この `pg_proc` に書き込まれたメタデータをエンジンがどう解釈するかに依存しています。インデックスをチューニングするのと同じ熱量で、関数定義のメタデータにも目を向けてみてください。
最後に
PostgreSQLは、オープンソースでありながら、商用DBにも劣らない極めて洗練されたアーキテクチャを持っています。その中枢を担う `pg_proc` を理解することは、単にクエリを書く力を超えて、PostgreSQLというエンジンの「思考プロセス」を理解することに繋がります。
次回のチューニングでは、`pg_proc` を `JOIN` して、プランナがなぜその選択をしたのか、その根拠を探ってみてください。きっと、これまで見えなかったボトルネックの正体が見えてくるはずです。
それでは、また次回の深掘りでお会いしましょう。ハッピー・クエリイング!
コメント