56

MySQL の楽観的ロックに関する詳細は見つかりません。トランザクションを開始すると、2 つのエンティティの更新が同期されたままになることを読みましたが、2 人のユーザーが同時にデータを更新して競合が発生することはありません。

どうやら楽観的ロックはこの問題を解決しますか? これは MySQL でどのように適用されますか。これにはSQL構文/キーワードはありますか? または、MySQL にはデフォルトの動作がありますか?

みんなありがとう。

4

1 に答える 1

170

要点は、オプティミスティック ロックはデータベース機能ではなく、MySQL やその他の機能でもないということです。オプティミスティック ロックは、DB を標準の手順で使用して適用されるプラクティスです。

非常に単純な例を挙げて、複数のユーザー/クライアントが同時に実行できるコードでこれを行いたいとしましょう:

  1. 1 つの ID フィールド (iD) と 2 つのデータ フィールド (val1、val2) を持つ行からデータを選択します。
  2. オプションで、データを使用して計算を行います
  3. その行の UPDATE データ

ロックしない方法は次のとおりです。

注: {中かっこの間} のすべてのコードは、(必然的に) 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}

OPTIMISTIC LOCKING の方法は次のとおりです。

- 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 つのことの組み合わせです。すべてがトランザクションなしで行われたことに注意してください! これが可能になった (トランザクションがない) のは、これが非常に単純な例であるためだけですが、これは、楽観的ロックの重要なポイントがトランザクション自体にないことも示しています。

では、TRANSACTIONS はどうでしょうか。

 - 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 は失敗します。

VERSIONING OPTIMISTIC LOCKING オプション:

 - 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。ここでトランザクションが存在しないのは、最初の例のように単純であるためであり、バージョン カラムの使用とは関係ありません。この列の使用は、データベース エンジンの機能ではなく、アプリケーション コードの実装次第です。

これ以外にも、この回答が長くなりすぎると思われる他のポイントがあります(すでに長すぎます)。

  • SELECT に対するトランザクションの影響に関するトランザクション分離レベル (ここでは MySQL の場合)。
  • 自動生成されていない主キー (または一意の制約) を持つテーブルに対する INSERT の場合、2 つのプロセスが一意でなければならない場所に同じ値を挿入しようとすると、特にチェックする必要なく自動的に失敗します。
  • id 列 (主キーまたは一意の制約) がない場合、単一の SELECT + UPDATE にもトランザクションが必要です。これは、他の人が変更を加えた後、UPDATE の WHERE 句の基準に一致する予想よりも多くの行があることに驚く可能性があるためです。

実際にチェックして自信をつける方法

分離レベルの値と実装は異なる可能性があるため、(このサイトではいつものように) 使用するプラットフォーム/環境でテストを実行することをお勧めします。

難しいように思えるかもしれませんが、実際には、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

次に、コマンドの順序と実行の順序を任意の順序で変更します。

于 2013-09-14T22:12:56.280 に答える