MyBatisPlusで使用されるオプティミスティックロッカー

MyBatisPlusで使用されるオプティミスティックロッカー

楽観的ロックの実装:

1. MyBatisPlusの更新操作中に、内部クエリ操作が最初に実行され、現在のバージョン= 1などの現在のバージョンが取得されます。

2.更新時に、バージョンはwhere条件文に含まれます(where id = 2やversion = 1など)。

3.更新を実行するときは、version = vesion +1を設定します。version= 1

4.以前に更新されていない場合、この時点でversion = 1、where句の条件が確立されます。以前に更新された場合、この時点でversion = 2であるため、where句の条件は確立されません。

乐观锁:1.先查询,获得版本号version比如version=1,然后调用MyBatisPlus中的updateById方法后,会在where的条件后面加上and version=1的条件,所以如果version=1的数据已经更新过了,那么此时version=2,所以where id=2 and version=1这个条件就不成立了,就会更新失败;如果之前没有更新过,那么此时version=1,所以where id=2 and version=1这个条件就成立,此时可以更新。
--A线程
update user set name="kuangshen",version=version+1
where id=2 and version=1
    
--B线程抢先完成,这个时候version=2.会导致A修改失败,因为where条件中的version=1条件条件不再满足
update user set name="kuangshen",version=version+1
where id=2 and version=1

MyBatisPlusで楽観的なロックプラグインをテストします

1.バージョンフィールドをデータベースに追加します

ここに写真の説明を挿入

2.エンティティクラスと対応するフィールド

ここに写真の説明を挿入

3.コンポーネントを登録します

つまり、以下に示すように、MyBatisPlus構成クラスを記述し、OptimisticLockerInterceptorプラグインを構成クラスに挿入します。

ここに写真の説明を挿入

4.テスト

更新が1つしかない場合は、以下に示すように、楽観的ロックが正常に更新できる必要があります。

ここに写真の説明を挿入

次の図に示すように、最初にデータを更新するために更新の途中でスレッドが挿入されたとすると、更新は失敗します。

ここに写真の説明を挿入

ここに写真の説明を挿入

実際、データの行を更新するときは、最初にクエリが実行され、次に更新するときに、クエリの結果を比較して一貫性があるかどうかを確認します。一貫性がない場合は、このデータの行がクエリから更新プロセスに更新されたことを証明します。変更後、この時点でデータを変更することはできなくなり、楽観的ロックにより、更新がエラーを回避できなくなります。一貫性がある場合は、クエリから更新までのプロセス中にデータが更新されていないことが証明されます。この時点で、楽観的ロックにより、その更新データ。

楽観的および悲観的なロック

個人的な要約:

オプティミスティックロックとペシミスティックロックは、主に同時実行の状況、つまり複数のスレッドがある場合に対処するために使用されます。ペシミスティックロックは、スレッドが実行されると、他のスレッドがブロックされた状態になることを意味します。このスレッドが実行されると、他のスレッドがブロックされます。 javaの基本を学ぶ前に、synchronizedキーワードはペシミスティックロックと同等であるため、他のスレッドがこのスレッドのデータに影響を与えることはありません。ペシミスティックロックはデータベース自体のロックメカニズムを使用します。

オプティミスティックロックは、スレッドの実行時に他のスレッドによって実行できます。オプティミスティックロックは、データベース自体のロックメカニズムを使用しませんが、データ自体に基づいてデータの正確性を保証します。

楽観的ロックと悲観的ロックはどちらも、ダーティリード、ファントムリード、および繰り返し不可能なリードを防ぐことができます。

Baidu検索

1つは、同時実行制御

プログラム並行性がある可能性がある場合は、並行性の状況でデータの正確性を確保して、現在のユーザーと他のユーザーが一緒に操作したときに、彼が単独で操作したときの結果と同じになるようにする必要があります。この方法は同時実行制御と呼ばれます。同時実行制御の目的は、あるユーザーの作業が別のユーザーの作業に不当に影響を与えないようにすることです。

同時実行制御を適切に実行しないと、ダーティリード、ファントムリード、繰り返し不可能なリードなどの問題が発生する可能性があります

img

よく参照される同時実行制御は、一般にデータベース管理システム(DBMS)に関連しています。DBMSの同時実行制御のタスクは、複数のトランザクションがデータベース内の同じデータに同時にアクセスするときに、データベースの分離、一貫性、および均一性が損なわれないようにすることです。

实现并发控制的主要手段大致可以分为乐观并发控制和悲观并发控制两种。
まず第一に、それが悲観的なロックであろうと楽観的なロックであろうと、それは人々によって定義された概念であり、一種の考えと見なすことができます。実際、これは、リレーショナルデータベースシステムにおける楽観的および悲観的なロックの概念だけでなく、休止状態、tair、memcacheなどの同様の概念でもありますしたがって、楽観的ロック、悲観的ロック、およびその他のデータベースロックを比較しないでください。楽観的ロックは、より多くの読み取りとより少ない書き込み(より多くのシナリオを読み取る)に適しており、悲観的ロックは、より少ない読み取りシナリオ(より多くの書き込みシナリオ)よりも多くの書き込みに適しています。

第二に、悲観的なロック(悲観的なロック)

1️⃣理解
データベース内のデータを変更する場合、他のユーザーによって同時に変更されないようにするための最善の方法は、データを直接ロックして同時実行を防ぐことです。データベースロックメカニズムを使用してデータを変更する前にロックしてから変更するこの方法は、ペシミスティック同時実行制御と呼ばれます[ペシミスティック同時実行制御、略して「PCC」、「ペシミスティックロック」とも呼ばれます]。

ペシミスティックロックは、その名前が示すように、強力な排他的および排他的特性を備えています。これは、外部の世界によって変更されるデータ(システムの他の現在の状況、および外部システムからのトランザクション処理を含む)に対する保守的な態度を指します。したがって、データ処理プロセス全体で、データはロックされます。ペシミスティックロックの実現は、多くの場合、データベースによって提供されるロックメカニズムに依存します(データベース層によって提供されるロックメカニズムのみがデータアクセスの排他性を真に保証できます。そうでない場合、ロックメカニズムがこのシステムに実装されていても、外部システムが変更されないことは保証できません。データ)。

img

これは、データ変更に対して悲観的な態度をとる同時実行制御方法であるため、悲観的ロックと呼ばれます。常に最悪のケースを想定します。データを読み取るたびに、他のスレッドがデフォルトでデータを変更するため、ロック操作を実行する必要があります。他のスレッドがデータにアクセスする場合は、ブロックしてハングする必要があります。悲観的なロックの実現:

  1. 従来のリレーショナルデータベースは、行ロック、テーブルロックなど、読み取りロック、書き込みロックなどのこのロックメカニズムを使用します。これらはすべて、操作の前にロックされます。
  2. Javaでのsynchronizedキーワードの実装。

2️⃣悲観的なロックは主に共有ロックと排他ロックに分けられます:

  • 共有ロック[共有ロック]は、読み取りロック、または略してSロックとも呼ばれます。名前が示すように、共有ロックとは、複数のトランザクションが同じデータのロックを共有し、データにアクセスできることを意味しますが、読み取りのみが可能で、変更はできません。
  • 排他的ロック[排他的ロック]は、書き込みロック、または略してXロックとも呼ばれます。名前が示すように、排他ロックは他のロックと共存できません。トランザクションがデータ行の排他ロックを取得すると、他のトランザクションは共有ロックや排他ロックなど、行の他のロックを取得できなくなりますが、排他ロックを取得するトランザクションは可能です。データ行を読み取って変更します。

3️⃣説明
悲観的な同時実行制御は、実際には「アクセスする前にロックをフェッチする」という保守的な戦略であり、データ処理のセキュリティを保証します。ただし、効率の観点から、ロックを処理するメカニズムにより、データベースに追加のオーバーヘッドが発生し、デッドロックの可能性が高くなります。さらに、並列処理が減少します。トランザクションがデータの行をロックする場合、他のトランザクションは、トランザクションが完了するのを待ってから、そのデータの行を処理する必要があります。

3、楽観的ロック(楽観的ロック)

1️⃣
楽観的ロックは悲観的ロックに関連していることを理解します。楽観的ロックは、データが通常の状況で競合を引き起こさないことを前提としています。したがって、データが送信および更新されると、データは正式に競合についてテストされます。競合が見つかった場合は、間違った情報がユーザーに返され、ユーザーが何をすべきかを決定できるようになります。楽観的ロックは、プログラムのスループットを向上させることができる多くの読み取り操作を伴うシナリオに適しています。

img

楽観的なロックメカニズムは、よりリラックスしたロックメカニズムを採用しています。楽観的ロックは比較的悲観的なロックであり、データベースのファントム読み取りや過度のビジネス処理時間によって引き起こされるデータ処理エラーを回避するメカニズムでもあります。ただし、楽観的ロックは、データベース自体のロックメカニズムを意図的に使用するのではなく、データ自体に基づいています。データの正確性を確保するため。楽観的なロックの実現:

  1. CAS実装:Javaのjava.util.concurrent.atomicパッケージの下のアトミック変数は、楽観的ロックのCAS実装を使用します。
  2. バージョン番号の制御:通常、データのバージョン番号バージョンフィールドがデータテーブルに追加され、データが変更された回数を示します。データが変更されると、バージョン値は+1になります。スレッドAがデータ値を更新する場合、データの読み取り中にバージョン値も読み取ります。更新を送信するときに、今読み取ったバージョン値が現在のデータベースのバージョン値と等しい場合は更新し、そうでない場合は再試行します。更新が成功するまで操作を更新します。

2️⃣説明
楽観的同時実行制御は、トランザクション間のデータ競合の可能性が比較的小さいと考えているため、可能な限り直接実行し、コミットするまでロックしないため、ロックやデッドロックは発生しません。

4、具体的な実現

1️⃣
悲観的ロックの実装悲観的ロックの実現は、多くの場合、データベースによって提供されるロックメカニズムに依存しています。データベースでは、悲観的ロックのプロセスは次のとおりです。

  1. レコードを変更する前に、レコードに排他ロックを追加してみてください。
  2. レコードが変更されていることを示すロックが失敗した場合、現在のクエリは待機するか、例外をスローする必要がある場合があります。具体的な対応方法は、開発者が実際のニーズに応じて決定します。
  3. ロックが正常にロックされると、レコードを変更できトランザクションの完了後にトランザクションのロックが解除されます。
  4. この期間中に、レコードを変更したり、排他ロックを追加したりする他の操作がある場合、ロック解除を待機するか、直接例外をスローします。

SQLでペシミスティックロックを使用する方法を説明する例として、より一般的に使用されるMySqlInnodbエンジンを取り上げます。

ペシミスティックロックを使用するには、MySQLデータベースの自動コミットプロパティをオフにする必要があります。MySQLはデフォルトで自動コミットモードを使用するため、つまり、更新操作が実行されると、MySQLはすぐに結果を送信します。(sqlステートメント:set autocommit = 0)

eコマース注文控除プロセスでの悲観的ロックの使用について説明します。

img

上記では、id = 1のレコードを変更する前に、最初にロックて更新し、次に変更します。これは典型的な悲観的なロック戦略です。

上記のインベントリを変更するためのコードが同時に発生する場合、1つのスレッドのみが同時にトランザクションを開き、id = 1のロックを取得でき、他のトランザクションはこのトランザクションが送信されるのを待ってから実行する必要があります。これにより、現在のデータが他のトランザクションによって変更されないことが保証されます。

上記のように、select ... for updateを使用するとデータがロックされますが、一部のロックレベル(MySQL InnoDBのデフォルトの行レベルロック)に注意する必要があります。行レベルのロックはインデックスに基づいています。SQLステートメントがインデックスを使用しない場合、行レベルのロックは使用されません。テーブルレベルのロックはテーブル全体をロックするために使用されます。これには注意が必要です。

2️⃣オプティミスティックロックの実装乐观锁不需要借助数据库的锁机制。

主に、競合の検出とデータの更新の2つのステップがあります。より一般的なのはCAS(比較と交換)です。

CASは比較して交換することです。これは、マルチスレッド並列処理でのロックの使用によって引き起こされるパフォーマンスの低下を解決するメカニズムです。CAS操作には、メモリ位置(V)、期待される元の値(A)、および新しい値(B)の3つのオペランドが含まれます。メモリ位置の値(V)が予想される元の値(A)と一致する場合、プロセッサは位置値を新しい値(B)に自動的に更新します。それ以外の場合、プロセッサは何もしません。いずれの場合も、CAS命令の前にその位置の値を返します。CASは、「位置(V)には値(A)が含まれている必要があると思います。値が含まれている場合は、新しい値(B)をこの位置に配置します。それ以外の場合は、位置を変更せず、現在の位置を教えてください。価値は十分です」。Javaでは、sun.misc.Unsafeクラスは、このCASを実装するためのハードウェアレベルのアトミック操作を提供します。java.util.concurrentパッケージの下にある多数のクラスは、このUnsafe.javaクラスのCAS操作を使用します。

複数のスレッドがCASを使用して同じ変数を同時に更新しようとすると、1つのスレッドのみが変数の値を更新でき、他のスレッドは失敗します。失敗したスレッドは一時停止されませんが、このコンテストで失敗したことが通知されます。そして、再試行できます。たとえば、以前の在庫控除の問題は、次のように楽観的なロックによって実現できます。

img

楽観的なロックの使用

更新する前に、まず在庫テーブルの現在の在庫数量(数量)を照会してから、更新を行う際の変更条件として在庫数量を使用します。更新が送信されると、データベーステーブル内の対応するレコードの現在の在庫番号が、最初に取り出された在庫番号と比較されます。データベーステーブル内の現在の在庫番号が、最初に取り出された在庫番号と等しい場合は、更新されます。期限切れのデータと考えてください。

上記の更新ステートメントには、深刻な問題、つまりABAの問題があります。img

  1. たとえば、スレッド1はデータベースからインベントリ番号3を取り出し、次にスレッド2もデータベースからインベントリ番号3を取り出し、スレッド2はいくつかの操作を実行して2になります。
  2. 次に、2番目のスレッドがインベントリカウントを再び3に変更します。このとき、最初のスレッドはCAS操作を実行し、データベースがまだ3であることを検出して、最初のスレッドが成功します。
  3. スレッド1のCAS操作は成功しましたが、プロセスに問題がないことを意味するわけではありません。

より良い解決策は、順次インクリメントできる単一のバージョンフィールドを渡すことです。最適化は次のとおりです。img

オプティミスティックロックは、データ変更操作を実行するたびにバージョン番号を保持します。バージョン番号がデータのバージョン番号と一致すると、変更操作を実行でき、バージョン番号を+1にすることができます。そうでない場合、実行は失敗します。各操作のバージョン番号が増えるため、ABAの問題は発生しません。タイムスタンプは自然に順番に増加するため、バージョンに加えて、タイムスタンプを使用することもできます。

上記のSQLには実際には特定の問題があります。つまり、高い同時実行性が発生すると、1つのスレッドのみが正常に変更でき、多くの失敗が発生します。TaobaoのようなeコマースWebサイトでは、高い同時実行性が一般的であり、ユーザーが失敗を認識するのは明らかに不合理です。したがって、楽観的なロックの粒度を減らす方法を見つける必要があります。より良い提案は、楽観的なロックの強度を減らし、スループットを最大化し、同時実行性を向上させることです!次のように:

img

上記のSQLステートメントで、ユーザーによる注文数が1の場合、quantity - 1 > 0楽観的なロック制御はメソッドによって実行されます。実行プロセスでは、アトミック操作で数量の値が1回照会され、1が差し引かれます。

同時実行性高い環境ロックの粒度制御は重要な科学です。適切なロックを選択すると、データのセキュリティを確保しながら、スループットとパフォーマンスを大幅に向上させることができます。

おすすめ

転載: blog.csdn.net/qq_45950109/article/details/112617266