MySQL транзакция с бухгалтерским приложением - PullRequest
4 голосов
/ 15 ноября 2008

У меня есть таблица, подобная следующей:

transaction_id
user_id
other_user_id
trans_type
amount

Эта таблица используется для ведения транзакций по счету для приложения типа "финансы".

Это учет двойной записи, поэтому при переходе от пользователя A к B две строки в таблице будут выглядеть следующим образом.

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, и для 3K одновременно поступают две транзакции, и обе они проходят, когда только одна должна.

Спасибо

Ответы [ 2 ]

6 голосов
/ 15 ноября 2008

Используете ли вы таблицы InnoDB или таблицы MyISAM? MySQL не поддерживает транзакции в таблицах MyISAM (но при попытке их использования не выдаст ошибку). Кроме того, убедитесь, что ваш уровень изоляции транзакции установлен правильно, он должен быть SERIALIZABLE, который не является значением по умолчанию для MySQL.

Эта статья содержит хороший пример, который объясняет влияние различных уровней изоляции на примере, очень похожем на ваш.

1 голос
/ 15 ноября 2008

Проблема в том, что понятие «учетная запись пользователя» «разбросано» по многим строкам в вашей таблице. С текущим представлением, я думаю, вы не можете «заблокировать учетную запись пользователя» (так сказать), поэтому вы открыты для условий гонки при их изменении.

Возможным решением было бы иметь другую таблицу с учетными записями пользователей и заблокировать строку в этой таблице, чтобы любой, кому нужно изменить учетную запись, мог попытаться получить блокировку, выполнить операцию и снять блокировку.

Например:

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;

... или что-то в этом роде.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...