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

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

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

}else{

echo '余额不足';
}

阅读 8.7k
9 个回答

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

有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的话,就提示报错

加字段 status 然后判断操作是否在执行,如果执行完毕就更改status 。在执行就不让操作呗

事务+共享锁或排它锁
session1:

start transaction;
select * from user where id =27 lock in share mode;
commit;

session2:

update user set money=100 where id =27

这时session2 是被锁住了,防止了并发

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
1 篇内容引用
推荐问题
宣传栏