mysql - 面试的时候遇到的问题,关于PHP并发
ringa_lee
ringa_lee 2017-04-10 15:44:45
[PHP讨论组]

大概就是 mysql的一张表存放着某个用户的余额,下面我写伪代码了。

$sql=select * from user wherer 余额 > 10 and id=1;
//如果有余额进行逻辑处理最后在减去扣款。
if($sql){
echo '请求它接口';
$sql=update user set 余额=余额-10;//关键就在这里了,如果减完了就相当于没余额了,但是由于并发,第二个人查询的时候是有的,他也进了这个条件。怎么避免这种情况

}else{

echo '余额不足';
}

ringa_lee
ringa_lee

ringa_lee

全部回复(7)
阿神

2种解决办法:”for update“的悲观锁,或者使用”版本号“的乐观锁,http://segmentfault.com/q/1010000002905539

大家讲道理

楼上说的"版本号"乐观锁不错,不需要数据库事务支持:
就像防止多人编辑一样,给表弄一个版本号字段.
获取数据时拿到版本号和余额,写入时比对版本号,相同则插入,并把版本号加1.

SELECT balance,version AS last_version FROM user WHERE id=1 AND balance>10;
UPDATE user SET balance=balance-10,version=version+1 WHERE id=1 AND version=last_version;
迷茫

$sql = select ... for update, 先给锁了试试

PHP中文网

有2种办法
方法1:
先查询一次余额,记为初始余额.
然后
$sql=update user set 余额=余额-10 where 初始余额>0
然后判断这条sql执行影响的行数,就可以避免并发导致余额为负的情况.
方法2:
$sql = update user set 余额=case when 余额>10 then 余额-10 else 余额 end where...
然后看影响行数.

黄舟

唉,题主你被坑了,这哪里是PHP问题,这明显是数据库问题。

正确的做法是,用存储过程 + 事务 + 锁。
而且一点都不简单,使用存储过程,你需要依次判断:
---->账号是否存在
---->余额够不够
---->数据修改后还要验证是否成功、是否修改正确(某些数据库有bug会导致没修改,或改错。因此自己要先用sql计算一次修改结果用于验证)

反对几种答案:
1.使用乐观锁。这要对表进行改动,增加last_version字段,明显不科学。
2.记录余额、或者在update时先where amount > 10,这也不科学,如果之前该用户的账号被删除了呢?
3.使用队列。以后系统的瓶颈集中在这里,看看老板会不会打死你。

伊谢尔伦

大并发可队列操作库。

伊谢尔伦

最简单的方式是在sql里加判断
update user set amount=amount-10 where amount > 10
如果执行影响列数为0的话,就提示报错

热门教程
更多>
最新下载
更多>
网站特效
网站源码
网站素材
前端模板
关于我们 免责申明 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送
PHP中文网APP
随时随地碎片化学习
PHP中文网抖音号
发现有趣的

Copyright 2014-2025 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号