MySQL の楽観的ロックに関する詳細は見つかりません。トランザクションを開始すると、2 つのエンティティの更新が同期されたままになることを読みましたが、2 人のユーザーが同時にデータを更新して競合が発生することはありません。
どうやら楽観的ロックはこの問題を解決しますか? これは MySQL でどのように適用されますか。これにはSQL構文/キーワードはありますか? または、MySQL にはデフォルトの動作がありますか?
みんなありがとう。
MySQL の楽観的ロックに関する詳細は見つかりません。トランザクションを開始すると、2 つのエンティティの更新が同期されたままになることを読みましたが、2 人のユーザーが同時にデータを更新して競合が発生することはありません。
どうやら楽観的ロックはこの問題を解決しますか? これは MySQL でどのように適用されますか。これにはSQL構文/キーワードはありますか? または、MySQL にはデフォルトの動作がありますか?
みんなありがとう。
要点は、オプティミスティック ロックはデータベース機能ではなく、MySQL やその他の機能でもないということです。オプティミスティック ロックは、DB を標準の手順で使用して適用されるプラクティスです。
非常に単純な例を挙げて、複数のユーザー/クライアントが同時に実行できるコードでこれを行いたいとしましょう:
注: {中かっこの間} のすべてのコードは、(必然的に) SQL 側ではなく、アプリ コード内にあることを意図しています。
- SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
- {code that calculates new values}
- UPDATE theTable
SET val1 = @newVal1,
val2 = @newVal2
WHERE iD = @theId;
- {go on with your other code}
- SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
- {code that calculates new values}
- UPDATE theTable
SET val1 = @newVal1,
val2 = @newVal2
WHERE iD = @theId
AND val1 = @oldVal1
AND val2 = @oldVal2;
- {if AffectedRows == 1 }
- {go on with your other code}
- {else}
- {decide what to do since it has gone bad... in your code}
- {endif}
重要なポイントは、UPDATE 命令の構造と、その後の影響を受ける行数のチェックにあることに注意してください。SELECT と UPDATE を実行したときに、間に誰かがすでにデータを変更したことをコードが認識できるようにするのは、これら 2 つのことの組み合わせです。すべてがトランザクションなしで行われたことに注意してください! これが可能になった (トランザクションがない) のは、これが非常に単純な例であるためだけですが、これは、楽観的ロックの重要なポイントがトランザクション自体にないことも示しています。
- SELECT iD, val1, val2
FROM theTable
WHERE iD = @theId;
- {code that calculates new values}
- BEGIN TRANSACTION;
- UPDATE anotherTable
SET col1 = @newCol1,
col2 = @newCol2
WHERE iD = @theId;
- UPDATE theTable
SET val1 = @newVal1,
val2 = @newVal2
WHERE iD = @theId
AND val1 = @oldVal1
AND val2 = @oldVal2;
- {if AffectedRows == 1 }
- COMMIT TRANSACTION;
- {go on with your other code}
- {else}
- ROLLBACK TRANSACTION;
- {decide what to do since it has gone bad... in your code}
- {endif}
この最後の例は、ある時点で競合をチェックし、他のテーブル/行を既に変更しているときに競合が発生したことを発見した場合.. ..その後、トランザクションを使用して、それ以降に行ったすべての変更をロールバックできることを示しています。始まり。明らかに、可能性のある衝突ごとにロールバックする操作の量を決定し、これに基づいて、トランザクション境界をどこに配置し、特別なUPDATE + AffectedRows チェック。
この場合、トランザクションでは、UPDATE を実行する瞬間とそれがコミットされる瞬間を分けています。では、この時間枠内に「他のプロセス」が更新を実行するとどうなるでしょうか? 何が起こるかを正確に知るには、分離レベルの詳細 (および各エンジンでの分離レベルの管理方法) を掘り下げる必要があります。READ_COMMITTED を使用する Microsoft SQL Server の例として、更新された行は COMMIT までロックされるため、「他のプロセス」はその行に対して何も実行できず (待機し続ける)、SELECT も実行できません (実際には READ_COMMITTED しか実行できません)。 )。したがって、「他のプロセス」アクティビティは延期されるため、UPDATE は失敗します。
- SELECT iD, val1, val2, version
FROM theTable
WHERE iD = @theId;
- {code that calculates new values}
- UPDATE theTable
SET val1 = @newVal1,
val2 = @newVal2,
version = version + 1
WHERE iD = @theId
AND version = @oldversion;
- {if AffectedRows == 1 }
- {go on with your other code}
- {else}
- {decide what to do since it has gone bad... in your code}
- {endif}
ここでは、値がすべてのフィールドで同じかどうかを確認する代わりに、専用のフィールド (UPDATE を実行するたびに変更される) を使用して、誰かが私たちよりも速く行を変更したかどうかを確認できることを示しています。 SELECT と UPDATE。ここでトランザクションが存在しないのは、最初の例のように単純であるためであり、バージョン カラムの使用とは関係ありません。この列の使用は、データベース エンジンの機能ではなく、アプリケーション コードの実装次第です。
これ以外にも、この回答が長くなりすぎると思われる他のポイントがあります(すでに長すぎます)。
分離レベルの値と実装は異なる可能性があるため、(このサイトではいつものように) 使用するプラットフォーム/環境でテストを実行することをお勧めします。
難しいように思えるかもしれませんが、実際には、DB 開発環境から 2 つの別々のウィンドウを使用し、それぞれでトランザクションを開始してコマンドを 1 つずつ実行することで、非常に簡単に実行できます。
ある時点で、コマンドの実行が無期限に続くことがわかります。次に、他のウィンドウで COMMIT または ROLLBACK が呼び出されると、実行が完了します。
ここで説明したように、すぐにテストできるいくつかの非常に基本的なコマンドを示します。
これらを使用して、テーブルと 1 つの有用な行を作成します。
CREATE TABLE theTable(
iD int NOT NULL,
val1 int NOT NULL,
val2 int NOT NULL
);
INSERT INTO theTable (iD, val1, val2) VALUES (1, 2 ,3);
次に、2 つの異なるウィンドウで次の手順を実行します。
BEGIN TRAN
SELECT val1, val2 FROM theTable WHERE iD = 1;
UPDATE theTable
SET val1=11
WHERE iD = 1 AND val1 = 2 AND val2 = 3;
COMMIT TRAN
次に、コマンドの順序と実行の順序を任意の順序で変更します。