数据库架构设计:使用并发跟踪用户余额

时间:2014-07-17 03:31:59

标签: sql database postgresql database-design finance

在我正在开发的应用中,我们的用户会在应用中存款并成为他们的余额。

他们可以使用余额执行某些操作并撤消余额。什么是模式设计,确保用户永远不会撤回/或执行更多,即使在并发中也是如此。

例如:

CREATE TABLE user_transaction (
  transaction_id SERIAL NOT NULL PRIMARY KEY,
  change_value   BIGINT NOT NULL,
  user_id        INT NOT NULL REFERENCES user
)

上述模式可以跟踪平衡,(从user_transction中选择sum());但是,这并不适用于并发性。因为用户可以同时发布2个请求,并且可以在2个并发数据库连接中插入两个记录。

我也不能进行应用程序内锁定(确保一次只能写入一个transcation),因为我运行了多个Web服务器。

是否有可以确保正确性的数据库架构设计?

P.S。在我的脑海中,我可以想象利用SQL中的唯一性约束。通过以后的事务引用先前的事务,并且由于每个早期事务只能被引用一次,这可以确保数据库级别的正确性。

1 个答案:

答案 0 :(得分:5)

每次插入新事务时依赖于计算帐户余额不是一个非常好的设计 - 首先,随着时间的推移,它会花费更长时间,因为越来越多的行出现在事务表中

更好的办法是将当前余额存储在另一个表中 - 新表或已用作外键引用的现有用户表。

看起来像这样:

CREATE TABLE users (
    user_id INT PRIMARY KEY,
    balance BIGINT NOT NULL DEFAULT 0 CHECK(balance>=0)
);

然后,每当您添加交易时,您都会像这样更新余额:

UPDATE user SET balance=balance+$1 WHERE user_id=$2;

您必须在交易中执行此操作,您还可以在该交易中插入交易记录。

并发问题会自动处理:如果您尝试从两个不同的事务中更新两次相同的记录,那么第二个将被阻塞,直到第一个提交或回滚。 “Read Committed”的默认事务隔离级别可确保这一点 - 请参阅手册section on concurrency

您可以从应用程序发出整个序列,或者如果您愿意,可以向user_transaction表添加触发器,这样无论何时将记录插入user_transaction表,余额都会自动更新。

这样,CHECK子句确保不会在数据库中输入任何会导致余额低于0的事务。