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。ここで、トランザクションがないのは、最初の例のように単純であるためであり、バージョン列の使用とは関係ありません。繰り返しになりますが、この列の使用はアプリケーションコードの実装次第であり、データベースエンジンの機能ではありません。
これ以外にも、この答えを長くしすぎる(すでに長すぎる)と思う他のポイントがあります。
分離レベルの値と実装は異なる可能性があるため、(このサイトでは通常のように)使用するプラットフォーム/環境でテストを実行することをお勧めします。
難しいように思えるかもしれませんが、実際には、2つの個別のウィンドウを使用し、トランザクションを1つずつ開始し、コマンドを1つずつ実行して、DB開発環境から非常に簡単に実行できます。
ある時点で、コマンドの実行が無期限に続くことがわかります。次に、他のウィンドウで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
次に、コマンドの順序と実行の順序を、考えられる任意の順序で変更します。