競合に対処する場合、2つのオプションがあります。
- 競合を回避するように試みることができ、それが悲観的ロックが行うことです。
- または、競合の発生を許可することもできますが、トランザクションをコミットするときに競合を検出する必要があります。これが楽観的ロックの機能です。
ここで、次のLost Updateの異常を考えてみましょう。
失われた更新の異常は、Read Committed分離レベルで発生する可能性があります。
上の図では、アリスが40を引き出すことができると信じているaccount
が、ボブが口座残高を変更したことに気づかず、この口座には20しか残っていないことがわかります。
悲観的ロック
悲観的ロックは、アカウントで共有ロックまたは読み取りロックを取得することでこの目標を達成し、ボブがアカウントを変更できないようにします。
上の図では、アリスとボブの両方が読み取りロックを取得します account
両方が、両方のユーザーが読み取っテーブル行のます。Repeatable ReadまたはSerializableを使用すると、データベースはSQL Serverでこれらのロックを取得します。
AliceとBobの両方account
がのPK値でを読み取ったため、1
1人のユーザーが読み取りロックを解放するまで、どちらもPK値を変更できません。これは、書き込み操作には書き込み/排他ロックの取得が必要であり、共有/読み取りロックは書き込み/排他ロックを防止するためです。
アリスがトランザクションをコミットし、account
行の読み取りロックが解放された後でのみ、ボブUPDATE
は再開して変更を適用します。アリスが読み取りロックを解放するまで、ボブのUPDATEはブロックされます。
データアクセスフレームワークが基になるデータベースの悲観的ロックサポートをどのように使用するかについて詳しくは、こちらの記事をご覧ください。
楽観的ロック
オプティミスティックロックでは競合が発生しますが、バージョンが変更されたときにAliceのUPDATEを適用すると競合が検出されます。
今回は、追加のversion
列があります。version
列は、UPDATEまたはDELETEが実行されるたびにインクリメントされ、そしてそれはまたWHERE UPDATEの句とDELETE文で使用されています。これを機能させるにはversion
、UPDATEまたはDELETEを実行する前にSELECTを発行して現在を読み取る必要があります。そうでない場合、WHERE句に渡すバージョン値またはインクリメントするバージョン値がわかりません。
データアクセスフレームワークが楽観的ロックを実装する方法の詳細については、こちらの記事をご覧ください。
アプリケーションレベルのトランザクション
リレーショナルデータベースシステムは70年代後半から80年代前半に登場し、クライアントは通常、ターミナルを介してメインフレームに接続していました。データベースシステムがSESSION設定などの用語を定義しているのは依然としてそのためです。
今日では、インターネットを介して、同じデータベーストランザクションのコンテキストで読み取りと書き込みを実行することはなくなり、ACIDでは十分ではなくなりました。
たとえば、次の使用例を考えてみます。
楽観的ロックがなければ、データベーストランザクションがSerializableを使用していても、この失われた更新がキャッチされることはありません。これは、読み取りと書き込みが別々のHTTPリクエストで実行されるため、異なるデータベーストランザクションで実行されるためです。
したがって、楽観的ロックは、ユーザー思考時間を組み込んだアプリケーションレベルのトランザクションを使用している場合でも、更新の損失を防ぐのに役立ちます。
アプリケーションレベルまたは論理トランザクションの詳細については、こちらの記事をご覧ください。
結論
オプティミスティックロックは非常に便利な手法であり、Read Committedなどの厳密性の低い分離レベルを使用している場合や、後続のデータベーストランザクションで読み取りと書き込みが実行されている場合でも問題なく機能します。
楽観的ロックの欠点はOptimisticLockException
、をキャッチしたときにデータアクセスフレームワークによってロールバックがトリガーされるため、現在実行中のトランザクションによって以前に実行したすべての作業が失われることです。
競合が多いほど、競合が多くなり、トランザクションが中止される可能性が高くなります。テーブル行とインデックスレコードの両方を含む可能性のある現在の保留中の変更をすべて元に戻す必要があるため、データベースシステムのロールバックはコストがかかる可能性があります。
このため、トランザクションがロールバックされる可能性を減らすため、ペシミスティックロックは競合が頻繁に発生する場合に適しています。