金融交易的交易隔离级别

问题描述 投票:0回答:1

假设我们有一个应用程序必须将资金从账户 A 转移到账户 B。 让我们假设数据库是 MySQL(尽管我也很欣赏 Postgres 的答案)。

Account A balance: 20
Account B balance: 0

Transaction 1 (TX1): transfer 10 from A to B
Transaction 2 (TX2): transfer 15 from A to B

TX1 和 TX2 同时发生。

现在,这是程序逻辑步骤:

  1. 从余额中读取
  2. 验证是否大于转账金额
  3. 保存更新。例如,在事务 1 中更新为:
    update BALANCES set balance = balance - 10 where id = A
    update BALANCES set balance = balance + 10 where id = B

问题:

1。该事务应该使用什么事务隔离级别?

2。事务隔离级别足够还是我必须显式使用悲观锁定?

让我详细说明一下。以 MySQL 中的 SERIALIZABLE 级别为例,当您在事务中读取记录时,记录会被共享锁锁定。 共享锁不会阻止其他事务放置共享锁并读取数据。

实际上,两个事务都获得了 A 和 B 上的共享锁,然后 TX1 尝试进行更新并接收排它锁。它不能这样做,因为 TX2 已经持有共享锁。 所以我们遇到了死锁:其中一个事务将被回滚,我们将不得不挂起一段时间(默认为 30 秒或更多秒)。

据我所知,仅通过可串行化隔离无法实现这样的转账。相反,看起来我们可以使用 READ_COMMITTED,但在读取数据时显式请求独占锁:“select * from BALANCES where id = A FOR UPDATE NOWAIT;”。因此,没有其他事务可以获得记录上的共享锁,从而防止死锁。

请帮助我理解这一点。

mysql postgresql transactions isolation-level transaction-isolation
1个回答
1
投票

你的结论是正确的,你需要显式地加锁来防止出现问题。换句话说,悲观锁定。

您用

FOR UPDATE
显示的语句实际上获取了独占锁,而不是共享锁。 MySQL 语法使用
FOR SHARE
来获取共享锁(这在 MySQL 5.x 中称为
LOCK IN SHARE MODE
)。

事实上,您还应该同时锁定 B 的目标行,以防止事务同时从 B 向 A 转账时出现死锁。

对于此任务来说,锁定比选择事务隔离级别更重要。这就是为什么当你问“我应该使用哪种事务隔离级别?”时没有明确的答案

© www.soinside.com 2019 - 2024. All rights reserved.