【问题标题】:How to implement a "bank transaction" like table?如何实现类似表的“银行交易”?
【发布时间】:2021-08-14 05:13:11
【问题描述】:

举个例子:

Operation Signal Value    Balance
1         +      100.00   100.00
2         +      50.00    150.00
3         +      10.00    160.00

现在我有两个并发事务,它们执行最后一个操作,添加值然后保存(插入)。这些值永远不会更新。

交易 1

获取最后一个操作 (3) 并将其锁定为“更新”并插入

Operation Signal Value    Balance
1         +      100.00   100.00
2         +      50.00    150.00
3         +      10.00    160.00
4         +      10.00    170.00

交易 2

获取最后一个操作并将其锁定“以进行更新”。由于第一个事务有一个“for update”锁,它等待获取最新的。在此示例中,事务 2 在事务 1 结束之前开始。

从 postgresql 返回的实际行是#3(在第一个锁被释放之后)。所以它最终是这样的:

Operation Signal Value    Balance
1         +      100.00   100.00
2         +      50.00    150.00
3         +      10.00    160.00
4         +      10.00    170.00
5         +      10.00    170.00

所以余额是 170.00。期望是 180.00。

这是 READ_COMMITTED 事务模式。

在 SERIALIZABLE 模式下,事务 2 会引发有关并发的错误。

我尝试了以下方法:

  • 用“for update”选择了最新的行;
  • 选择了一行this_is_the_last = true"for update",将其更改为false,然后插入一个带有this_is_the_last = true 的新行。 PostgreSQL 最终会返回带有 this_is_the_last = false 的行,即使它不是(在事务 1 释放 for update 锁之后的事务 2 上)。

有没有办法进行行级锁定并让事务 2 等待事务 1,以使事务 2 不会选择与事务 1 相同的“最新”?

【问题讨论】:

  • 这里需要使用SERIALIZABLE;你会得到并发错误,这意味着你必须回滚并重试。

标签: postgresql postgresql-12


【解决方案1】:

这种“丢失更新”永远不会在 PostgreSQL 中发生,即使您使用 READ COMMITTED 隔离。

operation定义为表的主键约束。

您的每笔交易都将执行:

INSERT INTO transaction (operation, signal, value, balance)
SELECT operation + 1,
       '+',
       10,
       balance + 10
FROM transaction
ORDER BY operation DESC
LIMIT 1;

那么如果有一个并发事务,两者之一将收到主键违规,因为operation 是唯一的。该事务只是重试操作,直到成功。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2011-09-10
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 2023-04-02
    • 1970-01-01
    • 2011-03-16
    • 1970-01-01
    相关资源
    最近更新 更多