1

我一直在开发一个应用程序来处理帐户和通过这些帐户进行的交易。

目前,应用程序使用的表按以下方式建模:

帐户
+----+-----------------+---------+-----+
| 编号 | 当前余额 | 版本 | ... |
+----+-----------------+---------+-----+
| 1 | 1000 | 48902 | ... |
| 2 | 2000 | 34933 | ... |
| 3 | 100 | 103 | ... |
+----+-----------------+---------+-----+

account_transaction
+--------+-------------+----------+----- --+------------------+--+
| 编号 | 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中插入一个新行,并且在account表中,它会更新存储帐户当前余额的列current_balance以及用于乐观锁定的列版本。如果乐观锁定有效,则提交事务,否则回滚事务。

作为一个粗略的例子,在处理事务 102 时,应用程序执行了以下伪 SQL/JAVA:

设置自动提交 = 0;

插入 account_transaction
(account_id、日期、值、结果金额)
价值观
(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_transaction1上的 AFTER INSERT 触发器中。

  • 首先,你是在事务结束时做的,所以即使事务很长,这一行的锁竞争也应该很短。
  • SQL 保证单个语句中的数据视图一致,因此不需要单独的SELECT ... FOR UPDATE.
  • 此外,由于您要添加一个值,而不是直接设置总和,因此这些操作的执行顺序并不重要 - 加法是可交换的(因此较短的事务可以“超越”较长的事务)。

1但要注意不要太早触发它 - 只有account_transaction在它完全“煮熟”时才插入新的,不要(例如)早插入但resulting_amount稍后更新。

于 2012-07-05T17:26:29.177 回答