2012-07-05 3 views
1

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

В настоящее время таблицы виды использования приложения моделируются следующим образом:

 
account 
+----+-----------------+---------+-----+ 
| id | current_balance | version | ... | 
+----+-----------------+---------+-----+ 
| 1 | 1000   | 48902 | ... | 
| 2 | 2000   | 34933 | ... | 
| 3 | 100    | 103  | ... | 
+----+-----------------+---------+-----+ 

account_transaction 
+------+-------------+----------------------+---------+------------------+-----+ 
| id | account_id |   date  | value | resulting_amount | ... | 
+------+-------------+----------------------+---------+------------------+-----+ 
| 101 | 1   | 03/may/2012 10:13:33 | 1000 | 2000    | ... | 
| 102 | 2   | 03/may/2012 10:13:33 | 500  | 1500    | ... | 
| 103 | 1   | 03/may/2012 10:13:34 | -500 | 1500    | ... | 
| 104 | 2   | 03/may/2012 10:13:35 | -50  | 1450    | ... | 
| 105 | 2   | 03/may/2012 10:13:35 | 550  | 2000    | ... | 
| 106 | 1   | 03/may/2012 10:13:35 | -500 | 1000    | ... | 
+------+-------------+----------------------+---------+------------------+-----+ 

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

В качестве грубого примера, при обработке транзакции 102, приложение сделал следующие псевдо SQL/JAVA:

 
set autocommit = 0; 

insert into account_transaction 
(account_id, date, value, resulting_amount) 
values 
(2, sysdate(), 550, 2000); 

update account set 
    current_balance = 2000, 
    version = 34933 
where 
    id = 2 and 
    version = 34932; 

if (ROW_COUNT() != 1) { 
    rollback; 
} 
else { 
    commit; 
} 

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

Как я могу эффективно обрабатывать текущий баланс для счетов? Текущий баланс необходим для авторизации/отказа от новых транзакций и используется в различных отчетах.

ответ

3

Как я могу эффективно обрабатывать текущий баланс для счетов?

Я думаю, что вся эта модель переработана.

Бросив оптимистическую блокировку через version и имеющий простой ...

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

... в конце сделки, которая вставляет новый account_transaction должно быть достаточно быстро. Для целостности данных подумайте о том, чтобы включить его в триггер AFTER INSERT по адресу account_transaction .

  • Прежде всего, вы делаете это в конце транзакции, поэтому даже если транзакция длинная, конфликт блокировки в этой строке должен быть коротким.
  • SQL гарантирует согласованный просмотр данных в одном заявлении, поэтому нет необходимости в отдельном SELECT ... FOR UPDATE.
  • Кроме того, поскольку вы являетесь , добавив значение вместо или непосредственно устанавливая сумму, на самом деле не имеет значения, в каком порядке эти операции выполняются - добавление является коммутативным (поэтому более короткие транзакции могут «обгонять» более длинные) ,

Но будьте осторожны, чтобы не вызвать его слишком рано - только вставить новый account_transaction когда он полностью «приготовленный», не (например) вставить в начале, но обновить resulting_amount позже.

+0

звучит хорошо, но один вопрос: не удалось ли поле result_amount быть неверным? Скажем, транзакция X и Y одновременно считывают текущий баланс и вставляют свои строки account_transaction одновременно. Хотя баланс будет правильным после обеих транзакций, не будет result_amount ошибочным для одной из строк? –

+0

@JoshNankin Мой ответ сосредоточился на 'current_balance', и он не будет работать так хорошо, если вам понадобится действительно« serial »' result_amount', как вы отметили. Попробуйте изменить порядок операций: сначала обновите 'current_balance', затем просто скопируйте новый баланс в новый' result_amount'. InnoDB должен блокировать UPDATE, эффективно сериализуя транзакции (на одной и той же «учетной записи» - другие учетные записи могут продолжаться одновременно) и избегать аномалии на INSERT. Это должно быть достаточно быстро - я предлагаю вам измерить, прежде чем пытаться реализовать оптимистичную схему блокировки. –

Смежные вопросы