Home  >  Article  >  Backend Development  >  mysql - 面试的时候遇到的问题,关于PHP并发

mysql - 面试的时候遇到的问题,关于PHP并发

WBOY
WBOYOriginal
2016-06-06 20:32:17982browse

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

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

}else{

echo '余额不足';
}

回复内容:

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

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

}else{

echo '余额不足';
}

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

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

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

<code>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;
</code>

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

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

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

大并发可队列操作库。

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

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

Statement:
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn