钱包余额使用的是记录和,但是现在负数了

\DB::transaction(function () {
//查询用户余额
$all_price = UserSales::query()->where([‘user_id’ => $this->user_id])->sum(‘price’);
if($all_price < $price)throw new \Exception(‘余额不足’)
})
现在有用户的余额是负数了,懵逼,看了下提交时间有3秒的时间差,难道数据写入有延迟?

路漫漫其修远兮,吾将上下而求索
《L04 微信小程序从零到发布》
从小程序个人账户申请开始,带你一步步进行开发一个微信小程序,直到提交微信控制台上线发布。
《G01 Go 实战入门》
从零开始带你一步步开发一个 Go 博客项目,让你在最短的时间内学会使用 Go 进行编码。项目结构很大程度上参考了 Laravel。
讨论数量: 18

修改余额的时候 直接用 update money = money - x from table where money > x,不要先查询再操作

11个月前 评论
pi_phq (楼主) 11个月前
Adachi (作者) 11个月前
Adachi (作者) 11个月前

如果在获取 $all_price 之后,其他进程有数据插入,那么这时候的 $all_price 还是上一次查询的结果...这种情况下,判断余额的条件其实是不准确的... 然后正常执行后续程序之后(插入数据),再求和的时候,就可能出现负数...

11个月前 评论
pi_phq (楼主) 11个月前
zhy (作者) 11个月前
pi_phq (楼主) 11个月前

楼主你这个错误是最基础的并发问题,应该对每一个用户在操作余额使用的时候加锁,常用方式使用文件锁或者行锁。

11个月前 评论
pi_phq (楼主) 11个月前

看看是不是存记录的地方逻辑有问题

11个月前 评论
pi_phq (楼主) 11个月前

解决方案是:

  1. 对单用户的扣减操作加锁,操作完成再释放
  2. 修改余额的时候可以这样
    $res = UserSales::query()
     ->where('user_id', $this->user_id)
     ->update([ 'price' => DB::raw('price-' . $price)]);
    if (!$res) {
     throw new \Exception ('余额不足')
    }
11个月前 评论
白胖子 11个月前
pi_phq (楼主) 11个月前
冯小胖同学 (作者) 11个月前
hhhzzz 11个月前

1.若不允许负数 字段设为 无符号 2.更新时加上 price >= xx ; 3.若有涉及并发 加锁 或放到队列里去处理

11个月前

讨论应以学习和精进为目的。请勿发布不友善或者负能量的内容,与人为善,比聪明更重要!