SELECT...FOR UPDATE是否延迟读取?商城余额并发操作技术咨询
关于并发操作中
SELECT...FOR UPDATE的行为解释 嘿,这个并发场景确实是在线支付/购物平台里非常典型的问题,我来给你拆解清楚你的两个疑问:
1. SELECT...FOR UPDATE是否会实际延迟读取?
其实不能叫“延迟读取”,更准确的说法是它会通过行级锁阻塞后续对同一行的读写操作,直到当前事务释放锁。当第一个事务执行SELECT user_balance FROM balances FOR UPDATE时,数据库会锁定该用户对应的balances行;此时第二个事务执行同样的语句时,会进入等待状态——直到第一个事务提交(或回滚)释放锁,第二个事务才能读取到最新的余额数据,而不是初始的100。
2. 两条SELECT...FOR UPDATE语句是否会同时执行?
不会,它们是串行执行的。具体流程是:
- 假设提现请求先到达,执行
SELECT...FOR UPDATE后成功锁定用户的余额行,读取到初始余额100; - 此时购表请求的
SELECT...FOR UPDATE会被阻塞,无法读取余额,必须等提现事务完成(提交或回滚); - 如果提现操作完成后余额被更新为0,那么购表请求的
SELECT...FOR UPDATE会读取到0,此时判断0 > 100不成立,就不会执行购表的更新操作,从根源上避免了余额变成负数的情况。
补充:你的代码需要注意的关键点
一定要确保SELECT...FOR UPDATE和后续的UPDATE操作在同一个数据库事务中执行!如果你的代码只是单独执行SELECT...FOR UPDATE,之后再执行UPDATE却没包裹在事务里,那么锁会在SELECT执行完成后立刻释放,这样还是会出现并发问题。正确的流程示例(伪代码):
// 提现页面事务包裹示例 $pdo->beginTransaction(); try { $withdrawal_amount = 100; $stmt = $pdo->prepare("SELECT user_balance FROM balances WHERE user_id = ? FOR UPDATE"); $stmt->execute([$user_id]); $user_balance = $stmt->fetchColumn(); if ($user_balance >= $withdrawal_amount) { $update_stmt = $pdo->prepare("UPDATE balances SET user_balance = user_balance - ? WHERE user_id = ?"); $update_stmt->execute([$withdrawal_amount, $user_id]); } $pdo->commit(); } catch (Exception $e) { $pdo->rollBack(); // 处理异常逻辑 }
这样就能保证整个“读取-判断-更新”的操作是原子性的,彻底杜绝并发导致的余额超支问题。
内容的提问来源于stack exchange,提问作者dramasea
相关产品推荐
相关产品推荐

