postgresql - 如果插入依赖于以前的记录,如何处理并发插入数据库?

标签 postgresql concurrency transactions ef-core-2.2

我正在设计一种小型银行系统(会有“信用”,而不是真钱)。如果用户有新交易,我想要一种 TransactionLog 表来获取记录,但我需要该记录必须包含字段“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]

我知道我可以使用表锁,但是我不想锁定整个表(对于成千上万的用户,它可能包含数百万条记录)。

是否有可能以某种方式仅针对“id 为 1 的用户的所有记录”锁定表? 或者是否有任何其他模式来处理上述情况?

我正在使用 EF Core 和 PostgreSQL。

最佳答案

不确定这在您的 ORM 中看起来如何,但最简单的技术是

  • “悲观锁”:

    在事务开始时,用

    锁定(不是整个表)
      SELECT ... FOR UPDATE
    

    然后尝试修改同一个账户的交易被序列化,并且不会发生异常。

  • 另一种选择是使用“乐观锁定”

    使用REPEATABLE READ 事务最方便地完成。如果两个事务试图同时读取并修改同一个帐户,其中一个将收到序列化错误

    您的软件必须准备好重复收到序列化错误的事务。

哪种方法更好取决于

  1. 您的数据库 API 对这些方法的支持程度

  2. 您预计发生碰撞的频率。

    对于频繁的碰撞,悲观锁定更好,因为它避免了频繁重复事务的需要(以更多锁定为代价)。

关于postgresql - 如果插入依赖于以前的记录,如何处理并发插入数据库?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/57686036/

相关文章:

postgresql - 如何在同一张表的 GROUP 内执行 LIMIT?

java - 如何使用 AtomicBoolean 阻塞和等待

Java 并发 : Synchronized(this) => and this. wait() 和 this.notify()

dictionary - golang线程安全映射, channel 作为线程安全的值

postgresql - 在 Postgres 函数循环中使用 RAISE NOTICE 访问 sql 聚合方法结果

ruby-on-rails - 未创建索引

jdbc - EJB、XA 事务和错误处理

PostgreSQL : Transaction and foreign key problem

ruby-on-rails - 每天查询 bool 列的累计和

java - 了解 EJB 事务类型