2

複数のサーバーの読み取りと書き込みでユーザークレジットを処理するための中央データベースがあります。アプリケーションはこれらのサーバーの上にあり、各リクエストに対して次のことを実行してユーザー リクエストを処理します。

1. データベースから読み取って、ユーザーがタスクに対して十分なクレジットを持っているかどうかを確認します。
2. 時間のかかるリクエストを実行する
3. ユーザー アカウントからクレジットを差し引き、新しいクレジット カウントを db に保存します。

アプリケーションはデータベースの楽観的ロックを使用します。したがって、次のことが起こる可能性があります

1. リクエスト a が入ってきて、ユーザー x に十分なクレジットがあることを確認し、
2. リクエスト b が届き、ユーザー x に十分なクレジットがあることを確認します。
3. 仕事をする
4. a は、新しいクレジット カウントを db に保存します。
5. b は仕事をする
6. b が新しいクレジット カウントを db に保存しようとすると、アプリケーションは例外を取得し、このクレジット控除の説明に失敗します。

悲観的ロックでは、アプリケーションはユーザー アカウントを明示的にロックして排他的アクセスを保証する必要がありますが、システムには多くの同時要求があるため、パフォーマンスが低下します。
では、この信用システムの優れた新しい設計は何でしょうか?

4

4 に答える 4

1

達成したいことを明確に述べていないので、信用度が低いために無駄だったことに気付くために仕事をしたくないと思います。

ノーロック

ステップ (1) で与信保留を実装し、作業 (2) と控除 (3) を保留に関連付けます。この方法では、信用度の低いユーザーはステップ (1) に合格しません。

楽観的ロック

オプティミスティック ロックのポスト ファクトムで衝突が検出されるため、想定に合わないと思います。

ペシミスティック・ロック

スキーマがわからないと断定はできませんが、パフォーマンスを殺すというのは言い過ぎだと思います。ユーザー アカウントを完全に排他的にロックするよりも、 MySQL InnoDBトランザクション分離レベル読み取りロックをより細かい粒度でスマートに組み込むことができます。たとえば、SELECT ... LOCK IN SHARE MODEwhich を使用して共有ロックを設定し、他のトランザクションの読み取りを許可します。

MySQL が待機するよりもタスク処理に時間がかかるという Rick の警告 ( innodb_lock_wait_timeout) がここに適用されます。

于 2015-03-31T12:55:22.573 に答える
1

2 つの理由のいずれかで InnoDB のロック メカニズムを使用しないようにするための 2 つの「ロック」メカニズムを以下に示します。

  • BEGIN...COMMITInnoDBで必要以上に時間がかかるタスク。
  • 開始時とは異なるプログラム (または異なる Web ページ) で終了するタスク。

プラン A. (これは、競合状態がまれであり、ステップ 2 の無駄な時間がそのようなまれなケースで許容されることを前提としています。)

  1. (同じ)データベースから読み取ることにより、ユーザーがタスクに十分なクレジットを持っているかどうかを確認します。
  2. (同じ) 時間のかかる要求を実行する
  3. (追加した)START TRANSACTION;
  4. (追加) 再度、ユーザーに十分なクレジットがあるかどうかを確認します。(ROLLABCKそうでない場合は中止します。)
  5. (古い #3 と同じ) ユーザー アカウントからクレジットを差し引き、新しいクレジット カウントを db に保存します。
  6. (追加した)COMMIT;

START..COMMITInnoDBトランザクションのものです。競合状態が原因でステップ 4 までに 'x' にクレジットがない場合はROLLBACK、ステップ 4 と 5 を実行し、実行しません。

プラン B. (これはより複雑ですが、こちらの方が好ましい場合もあります。)

  1. Locksロック用のテーブルを用意してください。user_id とタイムスタンプが含まれています。
  2. START TRANSACTION;
  3. user_id が の場合Locks、中止 (ROLLBACKおよび終了) します。
  4. INSERT INTO Locksuser_id と current_timestamp Locks(それにより「ロック」「x」)。
  5. COMMIT;
  6. 処理を実行します (元の手順 1、2、3)
  7. DELETE FROM Locks WHERE user_id = 'x';autocommit=1ここで十分です。)

潜在的な問題: 処理がステップ 6 で終了し、ロックの解放に至らない場合、そのユーザーは永久にロックアウトされます。Locks「解決策」は、 「非常に」古いタイムスタンプを定期的にチェックすることです。見つかった場合は、処理が終了したと見なし、行を削除します。

于 2015-03-31T05:44:12.033 に答える
1

MySQL を除くすべての実際のデータベース エンジンで見られるタイムスタンプ/行バージョン アプローチを使用します。

このようにして、MySQL でそれらをエミュレートできます。行が更新されるたびに更新される TIMESTAMP 列 (更新済み) があります。必要な残りのデータとともにその列を選択します。返されたタイムスタンプを WHERE 句の条件として使用して、タイムスタンプがまだ行を読み取ったときと同じ場合にのみ行が更新されるようにします。

UPDATE table SET col1 = value WHERE id = 1 AND updated = timestamp_value_read

更新を実行してタイムスタンプが一致しない場合、更新は実行されません。影響を受ける行を使用してこれをテストできます。0 行が更新された場合は、読み取りと書き込みの間で行が変更されたことがわかります。アプリケーションとユーザーにとって最適な方法でコード内のその条件を処理します。

タイムスタンプのチュートリアル

于 2015-04-01T22:51:25.227 に答える
1

エスクロー トランザクション メソッドが必要です。

各更新プロセスに一部を分配した後に残ったクレジットと、それらに分配された (つまり、エスクローに保持された) クレジットを記録します。プロセスは成功するまでトランザクションを再試行し、必要な分だけクレジットを増やし、残りのクレジットを減らします。クレジットが負でないままになる場合にのみ成功します。次に、長い計算を行います。計算が成功したかどうかに関係なく、次に、分配されたクレジットを減らすトランザクションを適用します。しかし、成功すると資産も増加し、失敗すると残りの信用が増加します。

于 2015-04-01T08:41:57.203 に答える