5

次のようなテーブルがあります。

transaction_id
user_id
other_user_id
trans_type
amount

このテーブルは、金融タイプのアプリのアカウント トランザクションを維持するために使用されます。

複式簿記であるため、ユーザー A から B への転送では、次のようにテーブルに 2 つの行が挿入されます。

1, A, B, Sent, -100
1, B, A, Received, 100

アカウントの残高は、そのアカウントのトランザクションを合計することによって計算されます。

例えば:

select sum(amount) from transactions where user_id=A

資金の移動をロックする最善の方法は何ですか? 私の現在のコードは次のようになります。

Start Transaction
Debit the sender's account
check the balance of the sender's account
if new balance is negative then the sender didn't have enough money and rollback
if the balance is positive then credit the receiver and commit

これは期待どおりに機能していないようです。基本的に言うトランザクションについてオンラインで多くの例を見ます:開始、デビット送信者、クレジット受信者、コミット。しかし、その間に送信者の残高を確認する最良の方法は何ですか?

通過してはならないトランザクションがあります。ユーザーが 3K の残高を持っていて、2 つのトランザクションが 3K に対してまったく同時に入ってきたとします。これらは両方とも、1 つだけが通過する必要があります。

ありがとうございました

4

2 に答える 2

6

InnoDB テーブルまたは MyISAM テーブルを使用していますか? MySQL は MyISAM テーブルでのトランザクションをサポートしていません (ただし、それらを使用しようとしてもエラーは発生しません)。また、トランザクション分離レベルが適切に設定されていることを確認してください。MySQL のデフォルトではない SERIALIZABLE にする必要があります。

この記事には、あなたと非常によく似た例を使用して、さまざまな分離レベルの影響を説明する良い例があります。

于 2008-11-15T16:39:42.240 に答える
1

問題は、「ユーザー アカウント」の概念がテーブル内の多くの行に「分散」していることです。現在の表現では、「ユーザー アカウントをロックする」ことはできないと思います (いわば)。

考えられる解決策は、ユーザー アカウントを持つ別のテーブルを用意し、そのテーブルの行をロックすることです。これにより、アカウントを変更する必要がある人は誰でもロックの取得、操作の実行、ロックの解除を試みることができます。

例えば:

begin transaction;
update db.accounts set lock=1 where account_id='Bob' and lock=0;
if (update is NOT successful) # lock wasn't on zero
  {
  rollback;
  return;
  }
if (Bob hasn't enough funds)
  {
  rollback;
  return;
  }

insert into db.transactions value (?, 'Bob', 'Alice', 'Sent', -3000);
insert into db.transactions value (?, 'Alice', 'Bob', 'Received',  3000);
update db.accounts set lock=0 where account_id='Bob' and lock=1;

commit;

...またはそのようなもの。

于 2008-11-15T20:28:00.110 に答える