トリガーは「劇薬」か、それとも「魔法の杖」か?
PostgreSQLを長年触っていると、若手エンジニアから「トリガーって使ってもいいんですか?」という質問をよく受けます。DBA界隈では「トリガーはデバッグが難しいから避けるべき」という格言(?)すらあるほどですが、私は少し違った見方をしています。
トリガーは、適切に使えばデータベースの堅牢性を担保する強力な武器ですが、内部の挙動を理解せずに闇雲に配置すれば、システムのパフォーマンスを食い潰す「見えない負債」にもなり得るのです。今回は、PostgreSQLのトリガーを単なる「入門」レベルから一歩進めて、アーキテクチャの深淵から掘り下げてみましょう。
—
実行タイミングの設計思想を読み解く
`CREATE TRIGGER` を叩くとき、私たちは `BEFORE`、`AFTER`、そして `INSTEAD OF` という選択肢と対峙します。これらは単なるタイミングの違いではなく、PostgreSQLの実行計画とトランザクション管理における決定的な違いです。
- BEFORE トリガー: データのバリデーションや正規化に最適です。ここで `NEW` レコードを書き換えることで、最終的にテーブルに書き込まれる値そのものを制御できます。ただし、クエリの実行直前に走るため、ここで重い処理(外部API呼び出しや複雑な集計)を行うと、その後の `INSERT/UPDATE` がすべて待たされることになります。
- AFTER トリガー: 制約チェックがすべて完了した後に走ります。監査ログの保存や、別テーブルへの非同期的な反映を行うのが定石です。
- INSTEAD OF トリガー: これは View に対する操作を抽象化するためのものですね。本来書き込めないビューに対して、裏側でどうデータを分配するかをプログラマが定義する。これは「データベースの抽象化」という観点では極めて強力なインターフェースになります。
—
FOR EACH ROW vs FOR EACH STATEMENT:パフォーマンスの分水嶺
ここからが少し専門的な話です。トリガーの実行単位である `FOR EACH ROW` と `FOR EACH STATEMENT` の使い分け、皆さん意識していますか?
特に `FOR EACH ROW` は危険な香りがします。100万行の `UPDATE` を走らせたとき、もしトリガーが `FOR EACH ROW` で設定されていたら、そのトリガー関数は100万回呼び出されます。PostgreSQLのトリガー関数は、呼び出されるたびにPL/pgSQLの実行コンテキストを構築するため、オーバーヘッドは馬鹿になりません。
パフォーマンス・チューニングの鉄則:
もし「対象の行数」に依存せず、操作全体に対して一度だけ処理すればいいもの(例えば、変更フラグの更新やバッチ処理のトリガー)であれば、迷わず `FOR EACH STATEMENT` を選ぶべきです。
—
パフォーマンストラブルシューティングの現場から
私が現場で遭遇する「トリガーが原因のパフォーマンス低下」は、大抵の場合、以下のパターンに集約されます。
1. トリガー内での重いクエリ:
トリガー内で `SELECT` を投げすぎてはいけません。トリガーは元のクエリのトランザクションの一部です。ここでコストの高いクエリを叩けば、その分だけロックの保持時間が伸び、デッドロックの温床になります。
2. 連鎖するトリガー:
トリガーAがテーブルBを更新し、それがトリガーBを起動する……という連鎖は、スタックオーバーフローや予期せぬロック競合を引き起こします。PostgreSQLの `pg_trigger` カタログと `EXPLAIN ANALYZE` を見て、トリガーによる実行時間が全体の何%を占めているかを可視化する習慣をつけましょう。
3. 可視性の欠如:
PostgreSQLの `auto_explain` を活用してください。`auto_explain.log_nested_statements = on` を設定することで、トリガー内で実行されたクエリもログに出力されるようになります。これがなければ、DBAは「なぜこのクエリがこれほど遅いのか」という迷宮に迷い込むことになります。
—
結論:トリガーを「正しく」飼いならすために
トリガーは便利です。アプリケーション側で実装すべきロジックをデータベースの整合性として強制できるからです。しかし、DBエンジニアとしての私の忠告は一つ。
「ビジネスロジックをトリガーに押し込めるな」
データベースはデータの守護者であって、複雑な業務フローを実行する場所ではありません。トリガーはあくまで「データの整合性を保つための最後の防波堤」として使い、アプリケーションロジックとデータベースの責務分界点を明確に保つ。このバランス感覚こそが、堅牢でスケーラブルなシステムを構築する鍵だと確信しています。
皆さんのプロジェクトのトリガーが、システムを支える魔法の杖として機能することを願っています。何か深掘りしたいテーマがあれば、またコメント欄やDMで教えてくださいね。では、良いPostgreSQLライフを!
コメント