保持更新的余额(会计)

nic*_*dnk 5 mysql

我一直在努力寻找一种方法来正确可靠地维护订单/付款系统中的帐户余额。

目前,该过程由三个表组成。下面的示例非常简化,但可以理解。

表一包含账户 ID、名称、支付总额、订单总数、余额等。

表二包含付款、日期、帐户 ID (FK) 和金额。

表三包含订单、日期、帐户 ID (FK) 和金额。

计算余额当然很简单

SELECT SUM(payments.AMOUNT) FROM payments WHERE ID = [account]
SELECT SUM(orders.AMOUNT) FROM orders WHERE ID = [account]
Run Code Online (Sandbox Code Playgroud)

接下来是一些简单的加法/减法数学:

UPDATE accounts SET BALANCE = [balance] WHERE ID = [account]
Run Code Online (Sandbox Code Playgroud)

一切都很好 - 问题是 - 我如何保持这一点,让我们假设多年的活动和许多交易?

当前,每次进行交易时都会更新帐户表,这意味着每次都会运行上述三个语句。当然,所有三个表中的 ID 列都有一个索引,现在一切都快速而整洁,但我处理的事务少于 100,000 个,这意味着所有内容都适合 RAM,基本上我扔给数据库的所有内容都会立即执行. 随着时间的推移,这当然会改变——那么我该如何处理呢?

将帐户标记为“脏”并稍后在批处理作业中处理平衡是否更好,或者即使我到达订单和付款表无法放入 RAM 的点,我是否可以继续实时更新余额?

我是否需要在给定时间之前(例如在上次计算之前)分割订单和付款并“锁定”所有内容?(我宁愿不)。我问是因为这样我就可以做一个 SUM WHERE DATE > [last lock timestamp] 并使用 UPDATE BALANCE = [balance] + [value for last lock] 进行更新,并防止问题超出资源。

我不需要每笔交易的运行余额。当我尝试研究这个问题时,这就是我所能找到的所有结果。我只关心支付的总金额、订购的总金额和账户余额。

你问我为什么在需要余额时不即时计算?因为人们必须能够查询系统并询问“我的(假设)50.000 个帐户中哪个帐户的余额低于 0?” - 如果我们不将余额存储在每个帐户中,我们就无法轻松做到这一点。

jka*_*lik 6

您可能应该以不同的方式考虑它:每笔交易都会根据某个值修改余额 - 它根据类型增加或减少。您可以根据当前余额以及新交易的价值和类型计算新余额。您通常不需要重新计算所有内容,因为之前已经完成了。当出现问题时,您可以使用这些数据来检查您的计算是否没有错误。

为了在您可能同时处理同一个帐户上的多个交易时保持平衡,您应该阅读有关锁定的内容。我建议在处理金钱或类似东西的情况下使用“悲观锁定”,当你阅读给定用户时锁定它的余额,然后计算和更新 - 你可以确定任何其他试图修改同一个帐户的线程都会有等到这个完成,所以它从最新的数据开始。这将需要事务和 InnoDB 或其他一些带有行锁的事务引擎,MyISAM 不会以这种方式工作(只能锁定整个表,这通常对性能来说要差得多)。

请注意,如果您在付款和订单中对 (ID, AMOUNT) 有索引,那么该索引可用于非常快速地计算大量交易的总和(假设这些表有很多列 - 日期时间,其他一些参考, 注释...) 因为它会更小并且以正确的方式组织(称为“聚类”),因此对于单个 ID,始终只需要读取连续的段。