这里是我的场景,假设我正在制作一个在线购物平台。我的用户有一个 100
在 user_balance
字段或表。
现在,用户,打开 withdrawal page
让他们 withdraw money
和一个 shopping page
这让他 buy a watch of 100 dollar with one click
假设用户提取100美元,同时以100美元的价格购买手表。
我的问题是 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
以下是两页代码的概念:
提款页:
$withdrawal_amount = 100;
$user_balance = "SELECT user_balance FROM balances FOR UPDATE"; //actually return 100?(not sure about it, that is what my question about)
if($user_balance > $withdrawal_amount){
//allow withdrawal
$update_sql_query = "UPDATE balances SET user_balance = user_balance - " . $withdrawal_amount;
}
购买观察页:
$product_subtotal = 100;
$user_balance = "SELECT user_balance FROM balances FOR UPDATE"; //actually return 100?(not sure about it, that is what my question about)
if($user_balance > $product_subtotal){
//allow withdrawal
$update_sql_query = "UPDATE balances SET user_balance = user_balance - " . $product_subtotal;
}
2条答案
按热度按时间f4t66c6m1#
是否选择更新延迟读取?
对。您需要使用事务来获得好的结果。
SELECT ... FOR UPDATE
,当在innodb表中的mysql事务中完成时,锁定选定的行。假设您的代码实际上只选择了一行SELECT something FROM balances WHERE id=something FOR UPDATE
.如果两个不同的程序连接到mysql,那么就尝试这样做
SELECT
在大致相同的时间在同一排,他们中的一个会赢。也就是说,它将首先到达那里,查询将完成。为了让它正常工作,把所有你需要做的工作都包起来
START TRANSACTION
以及COMMIT
. 比赛结束后你应该做的第一件事START TRANSACTION
应该是你的SELECT ... FOR UPDATE
.如果在你工作的时候,你决定用户不能做她想做的事情,你可以发布
ROLLBACK
代替COMMIT
交易中的所有更改都将被放弃。第二个程序的查询在第一个程序完成之前不会完成
COMMIT
完成交易。然后它将读取在该事务期间存储到表中的内容。要记住以下几点:对于连接到表服务器的其他程序,sql事务看起来就像一次发生一样。当一个程序有一个正在进行的事务时,其他程序等待。大多数时间事务都很快完成,因此很难观察等待时间。
2g32fytz2#
这里的正确方法似乎是使用
SELECT ... FOR UPDATE
. 在伪代码中,提取(或购买)的过程如下所示:这种模式在这里的作用如下。事务在正在更新的用户余额记录上获得独占锁。这意味着任何其他试图在用户余额被借记之前读取该余额的事务都将被阻塞,并且必须等待。这避免了两个事务交叉导致不正确余额的情况。
注意,锁定读取需要innodb引擎。有关更多信息,请查看mysql文档。