问大佬们一个问题,多进程下,数据库数据出现混乱

梦想世界
 public function cash(): Response
    {
        $param = postMore([
            ['member_id',1],
            ['phone',1],
            ['money',30],
            ['back_card',1],
            ['create_time',time()]
        ]);
        Db::startTrans();
        try {
            $this->member($param);
            $this->model->save($this->datatable,$param);
            Db::commit();
        } catch (Throwable $e) {
            Db::rollback();
            $this->error($e->getMessage(),$e->getCode());
        }
        return $this->success();
    }

    /**
     * 判断用户余额是否足够
     * 修改用户余额和提现中金额
     * @throws MyBusinessException
     */
    public function member($param)
    {
        $member = $this->model->findOne('back_member',['id' => $param['member_id']],'balance,txz_profit');
        if ($member['balance'] < $param['money']) {
            $this->error('用户余额不足',30001);
        }
        $member_data = [
            'balance' => bcsub($member['balance'],$param['money'], 2),//余额
            'txz_profit' => bcadd($param['money'], $member['txz_profit'], 2),//提现中金额
        ];
        $this->model->upd('back_member',['id' => $param['member_id']],$member_data);
    }

用户提现时,修改用户余额和提现中金额,然后增加一条提现记录。目前问题在于高并发多进程的情况下,提现记录增加了,但是修改用户余额的时候,数据可能并不是最新的。
目前测试改为单进程是没有这么问题。然后想问一下大佬有没有办法让这个请求始终在同一个进程中

283 2 2
2个回答

梦想世界

目前解决办法是改为单进程,或者使用

$lockFile = '/cash_member_lock'; // 锁文件路径
        $fp = fopen($lockFile, 'w');
        if (flock($fp, LOCK_EX)) { // 获取独占锁
            $param = postMore([
                ['member_id',1],
                ['phone',1],
                ['money',30],
                ['back_card',1],
                ['create_time',time()]
            ]);
            Db::startTrans();
            try {
                $this->member($param);
                $this->model->save($this->datatable,$param);
                Db::commit();
            } catch (Throwable $e) {
                Db::rollback();
                $this->error($e->getMessage(),$e->getCode());
            }
            flock($fp, LOCK_UN); // 释放锁
            fclose($fp);
        } else {
            $this->error('找不到锁');
        }

文件锁来控制始终在同一个经常中,

  • 暂无评论
six

因为你读取和写入是两步操作,不是原子操作,所以会有并发问题。用一个SQL就行了,一个SQL它是原子操作。
SQL类似update member set balance = balance - $value where member_id = x and balance >= $value
返回影响的行数为1代表成功,为0代表余额不足,这样不会有问题。

或者更改数据库事务隔离级别为序列化,这样即使你分成两步,数据库事务也是串行,不会有并发问题。

🔝