【问题标题】:How to handle concurrent insert to DB, if insert depends on previous record?如果插入依赖于先前的记录,如何处理对数据库的并发插入?
【发布时间】:2019-08-28 06:10:04
【问题描述】:

我正在设计一种小型银行系统(会有“信用”,而不是真钱)。如果用户有新的交易,我想要一种 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。

【问题讨论】:

    标签: postgresql concurrency transactions ef-core-2.2


    【解决方案1】:

    不确定这在您的 ORM 中的外观如何,但最简单的技术是

    • “悲观锁定”

      在事务开始时,用

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

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

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

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

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

    哪种方法更好取决于

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

    2. 您预计碰撞的频率。

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

    【讨论】:

      猜你喜欢
      • 2014-09-08
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-23
      • 2019-12-18
      • 2021-12-28
      • 1970-01-01
      • 2019-11-21
      相关资源
      最近更新 更多