13.4. アプリケーションレベルでのデータの一貫性チェック

PostgreSQLでのデータ読み取りはデータをロックしないので、トランザクションの隔離レベルに関係なく、あるトランザクションで読み込まれたデータは、同時に実行されているもう一方のトランザクションによって書き変えられる可能性があります。 言い換えると、SELECT で得た行は、その行が返ってきた時(現在の問い合わせが開始された後のある時点)に最新であるということを意味していません。 このトランザクションが開始した後であっても別のトランザクションがコミットしたことで、見ている行は更新、もしくは削除されてしまっている可能性があります。 "今"その行が有効であったとしても、現在実行中のトランザクションがコミットまたはロールバックで完了する前に(別のトランザクションによって)変更されたり、削除される可能性があります。

各々のトランザクションは特定のデータベースの内容のスナップショットを参照していて、同時に実行されているトランザクションが異なったスナップショットを参照している可能性が十分にあり得ると考えることもできます。 したがって、どちらにしても"今"という概念は少々おかしな定義となります。 これは通常、それぞれのクライアントアプリケーションがお互いに隔離されている場合は大きな問題ではありませんが、クライアント同士がデータベースの外で何かのチャンネルを使用して通信できる場合は大きな問題となり得ます。

現在有効な行を確実なものとし、同時に起こり得る更新を避けるためには、SELECT FOR UPDATE文やSELECT FOR SHARE文、適切なLOCK TABLE文を使用する必要があります (SELECT FOR UPDATE文やSELECT FOR SHARE文は返ってきた行のみを同時に起こる更新からロックし、LOCK TABLEはテーブル全体をロックします)。 これはPostgreSQLに他の環境からアプリケーションを移植する時に考慮されなければなりません

MVCCにおいてはグローバル有効性チェックに特別な考慮を払わなければなりません。 例えば銀行のアプリケーションで、テーブルにある全ての貸方の合計が、別のテーブルにある借方の合計と同じであることをチェックする必要があるとします。 そして、この両方のテーブルは常に更新されています。 2つの連続するSELECT SUM(...)コマンドの結果を比べると、2番目の問い合わせは、おそらく最初の問い合わせによってカウントされなかったトランザクションの結果を含んでいるため、リードコミッティドモードでは信頼のおける処理を実行できないことがわかります。 1つのシリアライザブルトランザクションで2つの合計を出力すると、シリアライザブルトランザクションが開始される前にコミットされたトランザクション結果の正確な状況を得ることができます。 しかし、その結果がもたらされた時点でもなお妥当であるかどうかは、実際には疑わしいかもしれません。 整合性チェックを行う前にシリアライザブルトランザクション自身が変更を行った場合、そのチェックの有効性はさらに疑わしくなります。 これにより、トランザクション開始後に行われる変更の全てだけでなく、何か別のものが含まれるためです。 このような場合、注意深い人であれば、現状を確実に把握するためにチェックに必要な全てのテーブルをロックするでしょう。 SHAREモード(もしくはそれ以上)のロックにより、現在のトランザクションでの変更を除き、ロックされたテーブルにコミットされていない変更が存在しないことが保証されます。

また、明示的なロック処理を使用して、同時に変更が実行されるのを防ごうとする場合、リードコミッティドモードを使用すべきであることに注意してください。 もしくは、シリアライザブルモードを使用する場合は、問い合わせを実行する前にロックを獲得するよう留意してください。 シリアライザブルトランザクションにおいて獲得されたロックは、テーブルに変更をかける他のトランザクションが現在実行されていないことを保証します。 しかし、トランザクションが参照しているスナップショットが、ロックの獲得より前に取得されたものであれば、そのスナップショットは現時点においてコミットされている変更より前のテーブルのものである可能性があります。 シリアライザブルトランザクションのスナップショットは、実際にはその最初の問い合わせもしくはデータ変更コマンド(SELECTINSERTUPDATE、またはDELETE)が開始された時点で取得されます。 したがって、スナップショットを取得する前に、明示的にロックを獲得することは可能です。

アダルトレンタルサーバー