SELECT .... FOR UPDATE实际上会延迟读取吗?

时间:2018-01-06 15:59:04

标签: php mysql sql

所以这是我的场景,让我们假设我正在建立一个在线购物平台。我的用户在100字段或表格中的余额为user_balance

现在,用户打开让他们withdrawal page的{​​{1}}和允许他withdraw money的{​​{1}}

假设用户提取100美元并同时购买100美元的手表。

我的问题是shopping page会同时执行还是等待其他人完成选择。

如果buy a watch of 100 dollar with one click同时执行,SELECT user_balance FROM balances FOR UPDATE将为这两个页面显示SELECT...FOR UPDATE,因此,它将允许提取user_balance并购买手表对于100因此,当我们最终更新用户的余额时,它将显示负余额

100

以下是两个页面的代码概念:

提款页​​面:

100

购买观察页面:

100(user balance) - 100(withdrawal amount) - 100(purchasing of watch) = -100

2 个答案:

答案 0 :(得分:2)

这里的正确方法似乎是使用SELECT ... FOR UPDATE在单独的事务中运行的每个操作。在伪代码中,撤销(或购买)的过程看起来像这样:

start transaction

SELECT user_balance FROM balances FOR UPDATE;
UPDATE balances SET user_balance = user_balance - 100;

end transaction

此模式的工作原理如下。该事务获得对正在更新的用户余额记录的独占锁定。这意味着在借记之前尝试读取用户余额的任何其他事务都将阻止,并且必须等待。这避免了两个事务交错的情况,导致不正确的平衡。

请注意,锁定读取需要InnoDB引擎。检查MySQL documentation以获取更多信息。

答案 1 :(得分:1)

  

SELECT FOR UPDATE会延迟读取吗?

是。您需要使用交易来获得良好的结果。

SELECT ... FOR UPDATE,当在InnoDB表中的MySQL事务内部完成时,锁定所选的一行或多行。让我们假设您的代码实际上只选择一行,SELECT something FROM balances WHERE id=something FOR UPDATE

然后,如果连接到MySQL的两个不同程序尝试在同一行上大约同时执行SELECT,则其中一个将获胜。也就是说,它将首先到达那里,然后查询将完成。

要使其正常运行,请在START TRANSACTIONCOMMIT中包含您需要完成的所有工作。 START TRANSACTION之后应该做的第一件事应该是你的SELECT ... FOR UPDATE

如果在您完成工作时决定用户无法执行其操作,您可以发出ROLLBACK代替COMMIT,并且将放弃交易中的所有更改

在第一个程序执行COMMIT完成其交易之前,第二个程序的查询将无法完成。然后它将读取在该事务期间存储在表中的任何内容。

这些是要记住的事项:SQL事务看起来,连接到表服务器的其他程序,就像它们一次发生的那样。当一个程序正在进行事务时,其他程序会等待。大部分时间交易都很快完成,因此很难观察等待时间。