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();
}
/**
* 判斷用戶余額是否足夠
* 修改用戶余額和提現(xiàn)中金額
* @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),//提現(xiàn)中金額
];
$this->model->upd('back_member',['id' => $param['member_id']],$member_data);
}
用戶提現(xiàn)時(shí),修改用戶余額和提現(xiàn)中金額,然后增加一條提現(xiàn)記錄。目前問題在于高并發(fā)多進(jìn)程的情況下,提現(xiàn)記錄增加了,但是修改用戶余額的時(shí)候,數(shù)據(jù)可能并不是最新的。
目前測(cè)試改為單進(jìn)程是沒有這么問題。然后想問一下大佬有沒有辦法讓這個(gè)請(qǐng)求始終在同一個(gè)進(jìn)程中
目前解決辦法是改為單進(jìn)程,或者使用
$lockFile = '/cash_member_lock'; // 鎖文件路徑
$fp = fopen($lockFile, 'w');
if (flock($fp, LOCK_EX)) { // 獲取獨(dú)占鎖
$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('找不到鎖');
}
文件鎖來控制始終在同一個(gè)經(jīng)常中,
因?yàn)槟阕x取和寫入是兩步操作,不是原子操作,所以會(huì)有并發(fā)問題。用一個(gè)SQL就行了,一個(gè)SQL它是原子操作。
SQL類似update member set balance = balance - $value where member_id = x and balance >= $value
。
返回影響的行數(shù)為1代表成功,為0代表余額不足,這樣不會(huì)有問題。
或者更改數(shù)據(jù)庫事務(wù)隔離級(jí)別為序列化,這樣即使你分成兩步,數(shù)據(jù)庫事務(wù)也是串行,不會(huì)有并發(fā)問題。