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

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

路漫漫其修远兮,吾将上下而求索
《L01 基础入门》
我们将带你从零开发一个项目并部署到线上,本课程教授 Web 开发中专业、实用的技能,如 Git 工作流、Laravel Mix 前端工作流等。
《L03 构架 API 服务器》
你将学到如 RESTFul 设计风格、PostMan 的使用、OAuth 流程,JWT 概念及使用 和 API 开发相关的进阶知识。
讨论数量: 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个月前

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