解决类似超卖问题,mysql加锁

情景:(代码环境Thinkphp)
我有一个流量充值接口,里面的操作大概是这样:
1.生产一个订单
2.查询用户余额是否足够,足够继续,不足提示错误
3.扣除用户余额,并生产账户记录
4.将订单设为已充值
$model ->startTrans();  // 启动事务
try   {
     $uInfo   = D( 'User' )->where( array ( 'id' => $uInfo [ 'id' ]))->find();
     // 判断用户余额是否足够
     if   ( $uInfo [ 'balance' ] <= 0 ||  $uInfo [ 'balance' ] <  $orderData [ 'total_price' ] ) {
        throw   new   \Think\Exception( "用户余额不足" ,5202);       
     }
     // 扣除用户余额,并生产账户记录
     if   ( !D( 'AccountRecord' )->consume( $orderData [ 'total_price' ], $orderData [ 'user_id' ], '充值流量,扣除余额' ) ) { 
         throw   new   \Think\Exception( "扣除用户余额失败" ,5302); 
     }
     // 将订单设为成功
     if   ( ! $model ->where( array ( 'id' => $order_id ))->save( array ( 'order_status' =>1)) ) {
         throw   new   \Think\Exception( "订单设置成功失败" ,5303);  
     }
     $model ->commit();  //事务提交
     echo   return_result( array ( 'code' => '2000' , 'msg' => '充值成功' )); exit ();
catch   (\Think\Exception  $e ) {
     $model ->rollback();  //事务回滚
     echo   return_result( array ( 'code' => $e ->getCode(), 'msg' => $e ->getMessage())); exit ();
}


这样看似没什么问题,一般情况下确实也没什么事,但是高并发情况下,就有问题,具体问题如下:
假如有个用户A同时调用了2次接口,A的账户余额为10,而一次的请求会扣除10的金额,那么,A的2次接口是同时进行的,2次一开始获取的用户余额都是10,这就是问题了
第一次请求,会将A的余额变为0,按理说,第二次请求会因为余额不足而不成功,但是,由于2次请求是并发一起进来的,那么第二次获取的余额上也是10,所以会继续扣款,
此时用户余额为-10

解决方法如下:
     $uInfo   = D( 'User' )->where( array ( 'id' => $uInfo [ 'id' ]))->find();
变成: $uInfo   = D( 'User' )-> lock(true) ->where( array ( 'id' => $uInfo [ 'id' ]))->find();
进行加锁后,成功将问题解决,but,有相对的代价,即性能增加了负担:

这是加锁前的:



这是加锁后的:


评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值