ストアドプロシージャの「深淵」—関数とプロシージャの境界線を理解する
PostgreSQLを長年触っていると、ふと「なぜ今さらプロシージャなのか?」と自問したくなる瞬間があるはずだ。PostgreSQLには歴史的に強力な`PL/pgSQL`関数がある。それにもかかわらず、PostgreSQL 11で導入された`CREATE PROCEDURE`という存在は、単なる「Oracleからの移植性を高めるためのオプション」以上の意味を持っている。
今日は、表面的な構文の話は飛ばして、この「プロシージャ」という存在をデータベースのアーキテクチャという観点から深掘りしてみよう。
—
関数(Function)とプロシージャ(Procedure)の決定的断絶
多くのエンジニアが躓くポイントは、関数とプロシージャの「トランザクション境界」の扱いだ。
結論から言えば、「関数はトランザクションの一部であり、プロシージャはトランザクションを支配できる」という点に集約される。
- 関数 (`FUNCTION`): 呼び出し元のトランザクションの文脈に完全に依存する。関数内で `COMMIT` や `ROLLBACK` を発行することは、PostgreSQLのアーキテクチャ上、許されない。それはSQLの冪等性や並列実行の整合性を守るための、いわば「神聖な制約」だ。
- プロシージャ (`PROCEDURE`): `CALL` コマンドで呼び出される際、自身の内部でトランザクションを制御できる。`COMMIT` や `ROLLBACK` を明示的に発行し、一連の処理を細切れにコミットしていくことが可能だ。
この違いが意味するのは、「長大なデータマイグレーションや、複雑なバッチ処理を単一のトランザクションで抱え込むリスク」からの解放である。
なぜプロシージャによるトランザクション制御が「諸刃の剣」なのか
プロシージャ内での `COMMIT` は非常に強力だが、同時に恐ろしい副作用も孕んでいる。
例えば、大量のレコードを更新する処理を、プロシージャを使って1,000件ごとにコミットするとしよう。アーキテクチャの観点で見れば、これは「長時間にわたる巨大なトランザクションによるWAL(Write Ahead Log)の肥大化」や「VACUUMの遅延」を回避する賢い戦略に見える。
しかし、現場でよく見る悲劇はこれだ。
- エラー発生時の部分的なコミット: トランザクションを細分化するということは、エラーで止まった際に「どこまで処理が終わっていて、どこからが未処理なのか」という状態管理(ステート管理)の責務を、開発者が背負うことになる。
- カーソルの維持: プロシージャ内でトランザクションをコミットすると、それまで開いていたカーソルは即座に無効化される。ループ処理でカーソルを使っている場合、コミットのタイミングを慎重に設計しないと、PostgreSQLは「Transaction aborted」という非情な宣告を突きつけてくる。
パフォーマンストラブルシューティングの勘所
もしあなたが、「プロシージャに変えたら逆に重くなった」という事象に遭遇したら、まず疑うべきは「不必要なコミットの頻度」だ。
コミットは物理的なディスクI/O(WALのフラッシュ)を伴う高コストな操作である。プロシージャでトランザクションを区切れるようになったからといって、1件ごとにコミットを叩くようなコードを書けば、データベースは即座にI/Oボトルネックに陥る。
熟練者がチェックすべき指標:
1. `pg_stat_activity` の監視: `wait_event_type` が `LWLock` で埋まっていないかを確認せよ。
2. WAL生成量: `pg_ls_waldir()` で生成速度を観測し、コミット頻度とWAL生成量のトレードオフを計算する。
3. ロック競合: トランザクションが短くなることで、むしろ他のプロセスとのロック競合の「ウィンドウ」が複雑化していないか。
結局、どう使い分けるべきか?
私のアドバイスはこうだ。
「読み取り専用」や「値の計算」が主目的であれば、迷わず `FUNCTION` を使え。関数の副作用を極限まで排除する設計は、クエリプランナの最適化にも優位に働く。
一方で、「データの整合性を担保しつつ、物理的に長大な処理を分割して実行しなければならない」という、実務上の泥臭い要件に直面したときだけ、`PROCEDURE` という武器を抜くべきだ。
プロシージャは、データベースという「整合性の要塞」の中に、あえて「制御可能なカオス」を持ち込むための道具だ。その力を正しく理解し、設計に組み込めたとき、あなたのデータベース設計は一歩先へ進むはずだ。
さて、次はどの機能を深掘りしようか。PostgreSQLの内部構造は、知れば知るほど底なしの面白さがある。また次回、コードの深層で会おう。
コメント