急遽セール、フラッシュセール、抽選、抽選等の場合は、売り切れを避けるため在庫数に限りがございますが、同時にご注文いただく人数が在庫数を超えた場合は、売られ過ぎの問題につながります。では、この問題をどのように解決すればよいでしょうか? 私のアイデアは次のとおりです (擬似コード): sql1: 製品在庫を問い合わせる if (在庫数量 > 0) { //注文を生成...
sql2: 同時在庫-1}
When thereは同時実行性がありません。上記のプロセスは正常に見えますが、2 人が同時に注文し、SQL1 ステージでは在庫が 1 つしかないため、両方の人がクエリした在庫が 0 より大きくなり、最終的に実行されます。 SQL2 をインストールすると、最終的に在庫は -1 になり、これは私たちが望む結果ではありません。
この問題を解決するための一般的なアイデアをまとめました。
1. 追加の単一プロセスを使用してキューを処理し、注文リクエストをキューに入れて 1 つずつ処理するため、同時実行の問題は発生しません。追加のバックグラウンド プロセスと遅延の問題は、当面はここでは考慮されません。ここではメッセージキューを使用できます。Memcacheq と Radis をよく使用します。 たとえば、ユーザーが取得できるチケットが 100 個ある場合、これらの 100 個のチケットをキャッシュに置き、読み取りおよび書き込み時にロックしないようにできます。 同時実行の量が多い場合、約 500 人がチケットを取得できる可能性があるため、500 人以降のリクエストはイベントの終了時に静的ページに直接転送されます。 500人中400人が商品を手に入れることは不可能です。そのため、列に並んだ順に先着100名のみが正常に購入できます。次の 400 名はイベント終了ページに直接移動します。もちろん、500 人と入力するのは単なる例です。その数は自分で調整できます。アクティビティ終了ページでは、データベースではなく静的ページを使用する必要があります。これにより、データベースへの負担が軽減されます。
2. MySQL の楽観的ロックとは、たとえば、合計在庫が 2 で、急ぎ購入イベントが送信されるとすぐに在庫が +1 になり、この時点で在庫が 3 になり、その後注文が生成されることを意味します。 、在庫は、在庫を更新する前に再度クエリされます (注文により、もちろん在庫 -1 が生成されますが、心配しないでください。在庫を再度確認すると、返される結果は 3 になります)。それが予想と一致しているかどうかを確認します。在庫数量 (ここでの予想在庫は 3) が一致しない場合はロールバックされ、在庫が不足していることをユーザーに通知します。ここでは、悲観的ロックについて説明します。じゃあ、楽観的ロックがあるはずだと思う人もいるかもしれません?? ここで、私が知っている悲観的ロックと楽観的ロックについて簡単に説明します
悲観的ロックと楽観的ロックは、2 つの一般的なリソース同時実行ロックです。設計のアイデアは、同時プログラミングにおける非常に基本的な概念でもあります。この記事では、データベース データに対するこれら 2 つの一般的なロック メカニズムの実装について、比較的かつ体系的に紹介します。
悲観的ロック
悲観的ロックの特徴は、最初にロックを取得してからビジネス操作を実行することです。つまり、「悲観的」はロックの取得が失敗する可能性が高いと考えているため、最初にロックが取得されたことを確認する必要があります。業務を行う前に正常に完了してください。一般に「1 つのロック、2 つのチェック、3 つの更新」と呼ばれるものは、悲観的なロックの使用を指します。一般に、データベースの悲観的ロックにはデータベース自体からのサポートが必要です。つまり、悲観的ロックは、一般的に使用される更新操作の選択を通じて実装されます。データベースは更新用選択を実行すると、選択内のデータ行の行ロックを取得します。そのため、同時に実行される他の更新用選択が同じ行を選択しようとすると、除外が発生します (行ロックが完了するまで待つ必要があります)。解除されます)のでロック効果が得られます。 select for update によって取得された行ロックは、現在のトランザクションの終了時に自動的に解放されるため、トランザクション内で使用する必要があります。
ここで注意すべき点は、データベースによって更新の選択の実装とサポートが異なることです。たとえば、Oracle は待機なしの更新の選択をサポートしています。これは、ロックを取得できない場合、代わりにエラーがすぐに報告されることを意味します。 MySQL には待機オプションはありません。 MySQL のもう 1 つの問題は、select for update ステートメントの実行中にスキャンされたすべての行がロックされ、問題が発生しやすいことです。したがって、mysql で悲観的ロックを使用する場合は、テーブル全体のスキャンではなく、必ずインデックスを使用してください。
楽観的ロック
楽観的ロックの特徴: 業務を優先し、必要な場合以外はロックを取得しないでください。つまり、私たちは「楽観的」であり、ロックが成功する可能性が最も高いと信じているため、ビジネス操作の完了後に実際にデータを更新する最後のステップの後にロックを取得するだけです。
データベースに対する楽観的ロックの実装は完全に論理的であり、データベースからの特別なサポートは必要ありません。一般的なアプローチは、ロックする必要があるデータにバージョン番号またはタイムスタンプを追加し、次のように実装することです。
1. SELECT data AS old_data, version AS old_version FROM …; に基づいてビジネス操作を実行します。データを取得し、new_data と new_version3 を取得します。 UPDATE SET data = new_data, version = new_version WHERE version = old_versionif (updated row > 0) { // 楽観的ロックの取得に成功、操作が完了しました } else { // 楽観的ロックの取得に失敗しました、ロールバックしますもう一度試してください}
実際には、楽観的ロックがトランザクション内にあるかどうかは関係ありません。基礎となるメカニズムは次のとおりです: データベース内の同じ行を更新する場合、同時実行は許可されません。つまり、データベースは更新された行の書き込みロックを毎回取得します。この行が正常に更新されるまで解放されません。そのため、業務を行う前にロックが必要なデータの現在の版数を取得し、実際にデータを更新する際に再度比較して前回と同じであることを確認し、バージョン番号を更新して、同時に変更が行われていないことを確認します。更新に失敗した場合は、古いバージョンのデータが同時に変更されて存在しなくなっていると考えられ、ロックの取得に失敗したものとみなされ、業務全体をロールバックする必要があります。必要に応じてプロセス全体を再試行できます。
これら 2 つのロックの概要は次のとおりです:
1. 楽観的ロックは、ロックの取得に失敗しない場合は悲観的ロックよりもオーバーヘッドが小さいですが、失敗が発生するとロールバックのオーバーヘッドが比較的大きいため、使用に適しています。ロックの失敗の可能性が比較的小さいシナリオでは、システムの同時実行パフォーマンスを向上させることができます。
2. 楽観的ロックは、業務運営中にデータベースへの接続を維持できない場合や、悲観的ロックを適用できないその他の場所など、一部の特殊なシナリオにも適しています。
3. 更新結果に基づいて判断し、sql2 に update table set inventory=xxx where inventory>0 が返された場合、在庫が不足していることを意味し、トランザクションがロールバックされます。
4. ファイル排他ロックを使用して、注文リクエストを処理するときに、flock を使用してファイルをロックします。その時点で、ユーザーは待機するか、直接処理する必要があります。 「サーバーがビジーです」というプロンプトが表示されます。おおよそのコードは次のとおりです:
ブロッキング (待機) モード
$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX)) { // 锁定当前指针,,, //..处理订单 flock($fp,LOCK_UN); }fclose($fp);
ノンブロッキング モード
$fp = fopen("lock.txt", "w+");if(flock($fp,LOCK_EX | LOCK_NB)) { //..处理订单 flock($fp,LOCK_UN); } else { echo "系统繁忙,请稍后再试"; }fclose($fp);
5. 分散クラスター サーバーの場合、Xiaomi と Taobao のラッシュ ショッピングは少し異なります。 . Xiaomi は重いです 掴んだ瞬間に、ノルマを掴めばそれはあなたのものとなり、注文して決済することができます。一方、淘宝網は支払い中のフィルタリングに重点を置いており、たとえば 10 個の商品を販売したい場合、支払い中に 10 人以上のユーザーが同時にフィルタリングを実行できるようにします。アイテムの数をレイヤーごとに瞬時に減らします。
6. redis lock product_lock_key をチケットロックキーとして使用します。 redis に product_key が存在する場合、すべてのユーザーが注文プロセスに入ることができます。 決済処理に入る場合は、まずsadd(product_lock_key, “1”)をredisに格納します。返却が成功したら決済処理に入ります。失敗した場合は、誰かがすでに支払いプロセスに入っていることを意味し、スレッドは N 秒間待機して、sadd 操作を再帰的に実行します。