2相コミット(2PC)の「光と影」:PostgreSQLで分散トランザクションを扱うということ
PostgreSQLを長年触っていると、「単一ノードで完結するACID」の快適さに甘えてしまいがちです。しかし、現実のシステムは残酷で、往々にしてデータを複数の物理ノードに跨がせて整合性を保つ必要に迫られます。
そこで登場するのが2相コミット(Two-Phase Commit, 2PC)。今回は、PostgreSQLの内部アーキテクチャの観点から、この「諸刃の剣」とどう付き合うべきか、僕なりの考察を書いてみます。
2PCの裏側:何がPostgreSQLの内部で起きているのか
PostgreSQLにおける2PCは、`PREPARE TRANSACTION`という強力なコマンドで実現されます。このコマンドが発行されると、何が起きるか。
一言で言えば、「トランザクションの状態がログ(WAL)に永続化され、かつロックが保持されたまま、一旦『中途半端な状態』で冷凍保存される」のです。
1. Prepareフェーズ: `PREPARE TRANSACTION ‘gid’`を実行すると、PostgreSQLは現在のトランザクションの変更内容をWALに書き込み、さらに`pg_twophase`ディレクトリに状態ファイルを生成します。この瞬間、このトランザクションは「コミットでもアボートでもない」特異点に入ります。
2. Commitフェーズ: 外部のトランザクションマネージャからの指示により`COMMIT PREPARED ‘gid’`が発行されると、ようやく永続的なコミット処理が完了します。
ここで重要なのは、「Prepareされたトランザクションは、たとえデータベースを再起動しても消えない」ということです。PostgreSQLは起動時に`pg_twophase`をスキャンし、未解決のトランザクションを復元します。これが整合性の担保の根幹です。
「孤児」トランザクション(Orphaned Transactions)という悪夢
2PCを運用するエンジニアが必ず直面するのが、「中途半端な状態で放置されたトランザクション」です。
例えば、分散トランザクションのコーディネーター側で障害が発生し、`COMMIT PREPARED`が発行されないまま残骸が残ることがあります。これを放置すると何が起きるか?
- ロックの解放待ちによるスループット低下: 該当行の行レベルロックは、そのトランザクションが解決されるまで永遠に保持されます。
- VACUUMの足止め: 最も怖いのがこれです。`xmin`が更新されないため、その古いトランザクションが参照しているデータ以降の行が一切掃除できなくなります。結果、テーブルは肥大化し、インデックススキャンは遅延し、システム全体が慢性的なパフォーマンス不全に陥ります。
もし皆さんのDBで`pg_prepared_xacts`ビューを覗いて、数日前のレコードが眠っていたら…即刻、調査とクリーンアップが必要です。
トラブルシューティングの勘所
2PCに関連するトラブルで、まず疑うべきポイントを整理しておきます。
- `max_prepared_transactions`の設定: デフォルトでは0に設定されていることが多いですが、これを適切にチューニングしていないと、分散トランザクションが失敗する原因になります。しかし、増やしすぎると共有メモリを圧迫するため、システム規模に応じたシビアな見積もりが必要です。
- WALの書き込みラグ: PrepareはWALへの物理的な同期書き込み(fsync)を伴います。高頻度で2PCを行うと、ディスクI/Oがボトルネックになり、単一ノードのトランザクションよりも遥かに高いレイテンシを観測することになります。
- コーディネーターの信頼性: 2PCは「コーディネーターが絶対に裏切らない(必ず最終的に解決する)」という前提で成り立っています。ネットワーク分断やコーディネーターのクラッシュに対するリカバリロジックが手薄だと、DB側は「宙吊り状態」を解消できず、手動介入を余儀なくされます。
最後に:2PCは「最後の手段」であるべき
僕が設計のアドバイスをする際、いつも言う言葉があります。「2PCを設計に組み込む前に、本当にその設計で良いのか、もう一度見直そう」と。
分散トランザクションは、システムに強力な整合性という恩恵をもたらしますが、同時に「可用性」と「運用コスト」を代償にします。マイクロサービス化が進む現在、Sagaパターンやイベントソーシングのような、2PCを使わない分散整合性の手法が主流になっているのは、この「2PCの重たさ」を誰もが痛感しているからです。
PostgreSQLの2PCは、非常に堅牢で信頼できる仕組みです。しかし、それを使いこなすということは、その背後にある「分散系の複雑さ」をすべて引き受けるという覚悟が必要です。
もし、あなたが今まさに2PCの実装に頭を悩ませているなら、まずは`pg_prepared_xacts`を監視するモニタリング体制を整えるところから始めてみてください。それが、この強力な機能を安全に扱うための、最初の第一歩です。
コメント