如果插入依赖于以前的记录,如何处理并发插入数据库?
How to handle concurrent insert to DB, if insert depends on previous record?
我正在设计一种小型银行系统(会有 'credits',不是真钱)。如果用户有新交易,我想要一种 TransactionLog table 来获取记录,但我需要此记录必须包含字段 "CreditsBefore" 和 "CreditsAfter".
考虑情况:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
假设用户“1”有 2 笔新交易。预期结果是:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
1 | +10 | 25 | 35
1 | +100 | 35 | 135
但如果它们并行执行,我们可能会得到:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
1 | +10 | 25 | 35
1 | +100 | 25 [bug] | 125 [bug]
我知道我可以使用 table 锁,但是我不想锁定整个 table(它可能包含数百万条记录,供成千上万的用户使用)。
是否有可能以某种方式锁定 table 只是为了 "all records for user with id 1"?
或者是否有任何其他模式可以处理上述情况?
我正在使用 EF Core 和 PostgreSQL。
不确定这在您的 ORM 中看起来如何,但最简单的技术是
“悲观锁定”:
在事务开始时,用
锁定行(不是整个table)
SELECT ... FOR UPDATE
然后尝试修改同一个帐户的事务被序列化,不会发生异常。
另一种选择是使用“乐观锁定”。
使用 REPEATABLE READ
交易最方便。如果两个事务试图同时读取并修改同一个帐户,其中一个将收到 序列化错误。
您的软件必须准备好重复收到序列化错误的事务。
哪种方法更好取决于
您的数据库API对方法的支持程度
您预计发生碰撞的频率。
对于频繁的碰撞,悲观锁更好,因为它避免了频繁重复事务的需要(以更多的锁为代价)。
我正在设计一种小型银行系统(会有 'credits',不是真钱)。如果用户有新交易,我想要一种 TransactionLog table 来获取记录,但我需要此记录必须包含字段 "CreditsBefore" 和 "CreditsAfter".
考虑情况:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
假设用户“1”有 2 笔新交易。预期结果是:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
1 | +10 | 25 | 35
1 | +100 | 35 | 135
但如果它们并行执行,我们可能会得到:
UserId | Transaction | CreditsBefore | CreditsAfter
1 | +20 | 0 | 20
1 | +5 | 20 | 25
1 | +10 | 25 | 35
1 | +100 | 25 [bug] | 125 [bug]
我知道我可以使用 table 锁,但是我不想锁定整个 table(它可能包含数百万条记录,供成千上万的用户使用)。
是否有可能以某种方式锁定 table 只是为了 "all records for user with id 1"? 或者是否有任何其他模式可以处理上述情况?
我正在使用 EF Core 和 PostgreSQL。
不确定这在您的 ORM 中看起来如何,但最简单的技术是
“悲观锁定”:
在事务开始时,用
锁定行(不是整个table)SELECT ... FOR UPDATE
然后尝试修改同一个帐户的事务被序列化,不会发生异常。
另一种选择是使用“乐观锁定”。
使用
REPEATABLE READ
交易最方便。如果两个事务试图同时读取并修改同一个帐户,其中一个将收到 序列化错误。您的软件必须准备好重复收到序列化错误的事务。
哪种方法更好取决于
您的数据库API对方法的支持程度
您预计发生碰撞的频率。
对于频繁的碰撞,悲观锁更好,因为它避免了频繁重复事务的需要(以更多的锁为代价)。