あなたがあなたの投稿で説明したことは、マルチユーザーシステムの一般的な状況です。異なるセッションは、同じテーブルと実際には同じ行を使用してトランザクションを同時に開始します。ここには2つの問題があります。
- セッションAがレコードを更新した後、セッションAがそのトランザクションをコミットする前に、セッションCがレコードを読み取るとどうなりますか?
- セッションCが、セッションAが更新したがコミットしていないのと同じレコードを更新した場合はどうなりますか?
(シナリオは、これらの問題の最初のもののみを示しています)。
最初の質問に対する答えは、ioslationレベルです。これは、セッション間でコミットされていないトランザクションの可視性の定義です。ANSI規格では、次の4つのレベルが指定されています。
- SERIALIZABLE:別のセッションからの変更は表示されません。
- REPEATABLE READ:ファントム読み取りが許可されています。つまり、同じクエリを2回実行すると、異なる結果が返される場合があります。
- READ COMMITTED:別のセッションによってコミットされた変更のみが表示されます。
- READ UNCOMMITTED:diryt readallowed、つまり、あるセッションからのコミットされていない変更が別のセッションに表示されます。
さまざまなフレーバーまたはデータベースがさまざまな方法でこれらを実装しており、すべてのデータベースがすべてをサポートしているわけではありません。たとえば、OracleはREAD COMMITTEDとSERIALIZABLEのみをサポートし、SERIALIZABLEをスナップショットとして実装します(つまり、読み取り専用トランザクションです)。ただし、マルチバージョン同時実行制御を使用して、READCOMMITTEDトランザクションでの繰り返し不可能な読み取りを防止します。
したがって、質問に戻ると、答えは次のとおりです。適切な分離レベルを設定します。適切なレベルは、データベースがサポートするレベルと、実行したい動作によって異なります。おそらく、READ COMMITTEDまたはSERIALIZABLEが必要です。つまり、トランザクションの開始と一致するデータ値に基づいてトランザクションを続行する必要があります。
他の問題に関しては、答えはもっと簡単です。トランザクションは、テーブルの更新を開始する前に、テーブルまたはできれば必要な行だけにロックを発行する必要があります。これにより、トランザクションはデッドロックを引き起こすことなくこれらの値の変更を続行できます。これは悲観的ロックと呼ばれます。接続プールを使用するアプリケーション(つまり、ほとんどのWebベースのアプリケーション)では不可能であり、状況ははるかに厄介です。