在我正在开发的应用中,我们的用户会在应用中存款并成为他们的余额。
他们可以使用余额执行某些操作并撤消余额。什么是模式设计,确保用户永远不会撤回/或执行更多,即使在并发中也是如此。
例如:
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中的唯一性约束。通过以后的事务引用先前的事务,并且由于每个早期事务只能被引用一次,这可以确保数据库级别的正确性。
答案 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的事务。