1

私は、アカウントとこれらのアカウントを介して行われたトランザクションを処理するアプリケーションを開発してきました。

現在、アプリケーションが使用するテーブルは次のようにモデル化されています。

アカウント
+ ---- + ----------------- + --------- + ----- +
| id | current_balance | バージョン| ... |
+ ---- + ----------------- + --------- + ----- +
| 1 | 1000 | 48902 | ... |
| 2 | 2000 | 34933 | ... |
| 3 | 100 | 103 | ... |
+ ---- + ----------------- + --------- + ----- +

account_transaction
+ ------ + ------------- + ---------------------- + ----- ---- + ------------------ + ----- +
| id | account_id | 日付| 値| 結果の量| ... |
+ ------ + ------------- + ---------------------- + ----- ---- + ------------------ + ----- +
| 101 | 1 | 2012年5月3日10:13:33| 1000 | 2000 | ... |
| 102 | 2 | 2012年5月3日10:13:33| 500 | 1500 | ... |
| 103 | 1 | 2012年5月3日10:13:34| -500 | 1500 | ... |
| 104 | 2 | 2012年5月3日10:13:35| -50 | 1450 | ... |
| 105 | 2 | 2012年5月3日10:13:35| 550 | 2000 | ... |
| 106 | 1 | 2012年5月3日10:13:35| -500 | 1000 | ... |
+ ------ + ------------- + ---------------------- + ----- ---- + ------------------ + ----- +

アプリケーションが新しいトランザクションを処理するたびに、account_transactionに新しい行を挿入し、アカウントテーブルで、アカウントの現在の残高と楽観的ロックに使用される列バージョンを格納する列current_balanceを更新します。楽観的ロックが機能する場合、トランザクションはコミットされます。機能しない場合、トランザクションはロールバックされます。

大まかな例として、トランザクション102を処理するとき、アプリケーションは次の疑似SQL/JAVAを実行しました。

autocommit=0に設定します。

account_transactionに挿入します
(account_id、date、value、resulting_amount)
値
(2、sysdate()、550、2000);

アカウントセットを更新する
    current_balance = 2000、
    バージョン=34933  
どこ
    id=2および
    バージョン=34932;

if(ROW_COUNT()!= 1){
    ロールバック;
}
そうしないと {
    専念;
}

ただし、特定のアカウントは非常にアクティブであり、多くの同時トランザクションを受信するため、アカウントテーブルの行を更新しているときにMySQLでデッドロックが発生します。これらのデッドロックは、データベースでデッドロックが発生したときにトランザクションが再処理されるため、アプリケーションに重大なパフォーマンスの低下をもたらします。

アカウントの現在の残高を効率的に処理するにはどうすればよいですか?現在の残高は、新しいトランザクションを承認/拒否するために必要であり、さまざまなレポートで使用されます。

4

1 に答える 1

3

アカウントの現在の残高を効率的に処理するにはどうすればよいですか?

このモデル全体は過剰に設計されていると思います。

楽観的ロックを放棄しversion、単純なものにする...

UPDATE account SET current_balance = current_balance + value WHERE id = ...

...新しいものを挿入するトランザクションの最後account_transactionには、十分に高速である必要があります。account_transactionデータの整合性のために、これを1のAFTERINSERTトリガーに入れることを検討してください。

  • まず、トランザクションの最後に実行するため、トランザクションが長くても、この行のロック競合は短くする必要があります。
  • SQLは、単一のステートメント内で一貫したデータビューを保証するため、個別のを必要としませんSELECT ... FOR UPDATE
  • また、値を加算するので、代わりに、または直接合計を設定するため、これらの操作が実行される順序は実際には重要ではありません。加算は可換です(したがって、短いトランザクションは長いトランザクションを「追い越す」ことができます)。

1ただし、トリガーが早すぎないように注意してaccount_transactionください。完全に「調理」されたときにのみnewを挿入します。たとえば、早めに挿入せず、resulting_amount後で更新します。

于 2012-07-05T17:26:29.177 に答える