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

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

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

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

Note: PostgreSQL バージョン 6.5 以前のものでは、読み込みロックを使用しているので、PostgreSQL のバージョン 6.5 以前のものからバージョンアップする際にも上記の考慮が必要となります。

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

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