標準SQLでは、同時に実行されるトランザクション間で防止されるべき3つの現象に対してトランザクションの隔離レベルを4レベルに分けて定義しています。3つの望ましくない現象とは下記のものです。ダーティリード同時に実行されている他のトランザクションが書き込んで未だコミットしていないデータを読み込んでしまう。反復不能読み取りトランザクションが、以前読み込んだデータを再度読み込み、そのデータが別のトランザクションによって更新されたことを見出す。(別のトランザクションが読み込み間隔の間にコミットした。)ファントムリードトランザクションが、複数行のある集合を返す検索条件で問い合わせを再実行したとき、別のトランザクションがコミットしてしまったために、同じ検索条件で問い合わせを実行しても異なる結果を得てしまう。4つの隔離レベルとその動作を表12-1に示します。安全PostgreSQLでは、4つの標準トランザクション隔離レベルを全て要求することができます。内部的には、リードコミッテッドとシリアライザブルに対応する2つの独立した隔離レベルのみがあります。リードアンコミッテッドレベルを選択した時、実際にはリードコミッテッドになり、リピータブルリードを選択した時、実際にはシリアライザブルになります。このように実際の隔離レベルは選択したレベルより厳密になることがあります。これは標準SQLでも許されています。この4つの隔離レベルについては、発生してはならない事象のみが定義され、発生しなければならない事象は定義されていません。いう仕組みに標準の隔離レベルを関連付ける実際的な方法がこれしかなかったことです。利用可能な隔離レベルについては後で詳細に説明します。TRANSACTIONコマンドを使用してください。リードコミッティド隔離レベルPostgreSQLでは、リードコミッティドがデフォルトの隔離レベルです。トランザクションがこの隔離レベルで実行されると、SELECT問い合わせはその問い合わせが実行される直前までにコミットされたデータのみを参照し、未だコミットされていないデータや、その問い合わせの実行中に別の同時実行トランザクションがコミットした更新は参照しません。(とは言っても、SELECT文は、自分自身のトランザクション内で実行され更新された結果はたとえまだコミットされていなくても参照します。結果として、SELECT問い合わせはその問い合わせが実行を開始した時点のデータベースのスナップショットを参照することになります。単一のトランザクション内であっても、SELECT文を2回連続して発行した場合、最初のSELECT文を処理している最中に他のトランザクションが更新をコミットすると、最初とその次に発行したSELECT問い合わせは異なるデータを参照してしまうことを知っておいてください。UPDATEコマンドは対象行を検索する際にSELECTコマンドと同じように振る舞います。これらのコマンドは、問い合わせが開始された時点で既にコミットされている対象行のみを検出します。(もしくは削除、もしくは更新対象としてマーク)されてしまっているかもしれません。このような場合更新されるべき処理は、最初の更新トランザクションが(それがまだ進行中の場合)コミットもしくはロールバックするのを待ちます。最初の更新処理がロールバックされるとその結果は無視されて、2番目の更新処理で元々検出した行の更新を続行することができます。最初の更新処理がコミットされると、2番目の更新処理では、最初の更新処理により行が削除された場合はその行を無視します。行が削除されなかった時の更新処理は、最初のコミットで更新された行に適用されます。(WHERE句)は、更新された行がまだその検索条件に一致するかどうかの確認のため再評価されます。番目の更新処理は、更新された行から処理を開始します。このような仕組みにより、更新コマンドが、互いに矛盾したスナップショットを参照する可能性があります。これらの問い合わせは、互いが更新しようとしている同じ行に影響を及ぼす同時実行中の更新問い合わせによる結果を参照できますが、データベース中の他の行に対する同時実行の問い合わせの結果は参照できません。このような動作をするために複合検索条件を含む問い合わせにリードコミッティドモードを使用することは適切ではありません。単純な検索条件の場合、このモードの使用が適しています。銀行の残高を更新する以下のようなトランザクションを考えてみます。7534;COMMIT;同時に実行される2つのトランザクションが、口座番号12345の残高を変更しようとした場合、口座12345の行が更新されてから2番目のトランザクションを開始すべきです。各コマンドが事前に決定されていた行にのみ処理を行なうため、更新されたバージョンの行は問題となる不整合を引き起こしません。リードコミッティドモードでは、新規の各コマンドは、その時点でコミットされている全てのトランザクションを含めた新規のスナップショットを使用して実行を開始するため、同一トランザクション内の後続のコマンドでは、いかなる場合でも、コミットされた同時実行中のトランザクションの結果を参照することになります。この問題でのポイントは、単一のコマンド内で、完全に整合しているデータベースのビューを参照しているかどうかということです。リードコミッティドモードで提供されている部分的なトランザクション隔離は、多くのアプリケーションでは適切です。このモードは高速で、使い方も簡単です。複雑な問い合わせや更新を行うアプリケーションでは、リードコミッティドモードで提供される以上に、データベースに対して厳密に一貫性のある見え方を保障する必要があるかもしれません。シリアライザブル隔離レベルシリアライザブルレベルは、トランザクションの隔離としては最も厳密なものです。このレベルではトランザクションが同時にではなく、次から次へと、あたかも順に実行されているように遂次的なトランザクションの実行をエミュレートします。このレベルを使ったアプリケーションでは、直列化の失敗によるトランザクションの再実行に備えておく必要があります。トランザクションがシリアライザブル隔離レベルにある時にSELECT問い合わせを実行すると、トランザクションが開始される前までにコミットされたデータのみを参照します。コミットされていないデータや、そのトランザクションの実行中に別のトランザクションで更新されたデータは参照しません。(しかし、SELECT文では、そのトランザクションで行われた、まだコミットされていないデータを参照します。SELECT文では、トランザクション内のこの問い合わせが行われ始めた時点ではなく、トランザクションそのものが始まったときの状態のスナップショットを参照するという点でリードコミッティドレベルとは異なっています。従って、単一トランザクション内の連続するSELECT文は、常に同じデータを参照していることになります。UPDATEコマンドでは、SELECTと同じように対象行を検索します。これらのコマンドでは、トランザクションが開始された時点でコミットされている対象行のみを検出します。(もしくは削除、もしくは更新対象としてマーク)されている可能性があります。このような場合、シリアライザブルトランザクションは、最初の更新トランザクションが(それらがまだ進行中の場合)コミットもしくはロールバックするのを待ちます。最初の更新処理がロールバックされると、その結果は無視され、シリアライザブルトランザクションでは元々検出した行の更新を続行することができます。最初の更新処理がコミット(かつ、単に更新のために選択されるだけでなく、実際に行が更新または削除)されると、シリアライザブルトランザクションでは、以下のようなメッセージを出力してロールバックを行ないます。updateこれは、シリアライザブルトランザクションでは、トランザクションが開始された後に別のトランザクションによって更新されたデータは変更できないためです。アプリケーションがこのエラーメッセージを受け取った場合、現在のトランザクションを中断して、トランザクション全体を始めからやり直されなければなりません。2回目では、トランザクションはコミットされた変更含めてデータベースの最初の状態とみなすので、新しいバージョンの行を新しいトランザクションにおける更新の始点としても、論理的矛盾は起こりません。更新トランザクションのみ再実行する必要があるかもしれません。読み込み専用トランザクションでは直列化の衝突は決して起こりません。シリアライザブルモードでは、全てのトランザクションが一貫したデータベースの状態を参照できることが保障されます。同時にトランザクションの更新を行うことで、今までずっと逐次実行しているように見せかけてきたものが破綻してしまいそうな場合、アプリケーションではトランザクションを再実行する準備をしておく必要があります。複雑なトランザクションを再実行する際のコストが無視できないほど大きくなる可能性があるため、このモードは、リードコミッティドモードでは誤った結果を表示させてしまう可能性がある、かなり複雑なロジックを有する更新トランザクションを実行する場合にのみ使用することをお勧めします。ほとんどの場合、シリアライザブルモードは、データベースの同一ビューを参照する必要のある複数の連続する問い合わせをトランザクションが処理する際に必要です。シリアライザブル隔離と本当の直列性"シリアライザブル"な実行の直感的な意味(および数学的な定義)は、コミットに成功した2つの同時実行のトランザクションは、厳密に直列的に、つまり1つ1つ順番に実行されるように見えることです。どちらが先に現れるかを前もって予測することができません。表12-1に示した直列化できない振舞いを禁止することが真の直列性を完全に保障しないことを認識することは重要です。実際PostgreSQLのシリアライザブルモードはこの点のため直列化実行を保障していません。以下の初期データを持つmytabというテーブルを考えてます。2の行を挿入したとします。1の行として挿入します。ここで両方のトランザクションがコミットします。ここでは前述の直列化できない振舞いはありませんが、どちらを先に直列化したとしても得ることができない結果になります。AがBより前に実行したとすると、Bの総和は300ではなく330と計算されます。同様に別の順序で実行されたとしてもAで計算される総和は異なる結果になります。数学的に正しい直列性を保障するには、データベースシステムは、同時実行トランザクションの問い合わせのWHERE条件に一致する行を、他のトランザクションからは挿入、変更ができないことを意味する述部ロック処理を強制的に行う必要があります。1という問い合わせを行った時、述部ロック処理システムは、トランザクションAがコミットされるまで、トランザクションBがclassが1の行を新しく挿入することを禁止します。全てのセッションが、全ての同時実行トランザクションで実行された全ての問い合わせの細部に注意しなければなりませんので、このようなロック処理システムの実装は複雑で、その実行はかなり高価です。実際にはほとんどのアプリケーションはこうした問題が生じる可能性がある操作を行いませんので、こうした高価な作業の大部分は無駄になります。(確かに上の例はわざと考えたものであり、実際のソフトウェアで行われる可能性はほぼありません。従って、PostgreSQLは述部ロック処理を実装していません。私たちが知っている限りにおいて、他のDBMS製品でも実装されていません。直列実行されない可能性があることが本当に障害になる場合は、明示的なロック処理を適切に使用することで問題を解決することができます。以下の節でより詳細に説明します。注意[1]基本的に、述部ロックシステムは書き込まれるものを制限することでファントムリードを防ぎ、MVCCは読み込まれるものを制限することでファントムリードを防ぎます。