I’ve been developing an application that handles accounts and transactions made over these accounts.
Currently the tables the application uses are modelled the following way:
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 | ... | +------+-------------+----------------------+---------+------------------+-----+
Whenever the application processes a new transaction, it inserts a new row into account_transaction and, at the account table, it updates the column current_balance that store the current balance for the account and the column version used for optimistic locking.
If the optimistic locking works, the transaction is commited, if it doesn’t the transaction is rolled back.
As a rough example, when processing the transaction 102, the application did the
following pseudo 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;
}
However certain accounts are very active and receive many simultaneous transactions which causes deadlocks at MySQL while updating the rows at account table. These deadlocks impose a serious performance penalty to the application since it causes the transactions to be reprocessed when deadlocks at the database occur.
How can I efficiently handle the current balance for the accounts? The current balance is needed to authorize/deny new transactions and is used in various reports.
I think this whole model is over-engineered.
Abandoning the optimistic locking through
versionand having a simple……at the end of the transaction that inserts a new
account_transactionshould be plenty fast. For data integrity, consider putting this into AFTER INSERT trigger onaccount_transaction1.SELECT ... FOR UPDATE.1 But be careful not to trigger it too early – only insert new
account_transactionwhen it is completely “cooked”, don’t (for example) insert early but update theresulting_amountlater.