国产+高潮+在线,国产 av 仑乱内谢,www国产亚洲精品久久,51国产偷自视频区视频,成人午夜精品网站在线观看

[已解決]webman多進(jìn)程導(dǎo)致mysql死鎖

MarkGo

使用的是 "illuminate/database": "^9.3"

最近業(yè)務(wù)上線,發(fā)現(xiàn)經(jīng)常產(chǎn)生死鎖。
排查過(guò)sql語(yǔ)句,并沒(méi)有發(fā)現(xiàn)什么異常

隔離級(jí)別是Read committed,
執(zhí)行update的時(shí)候也是根據(jù)主鍵ID進(jìn)行的。

隔三差五的就會(huì)導(dǎo)致mysql死鎖,通過(guò)KILL把死鎖的KILL了也沒(méi)用,后續(xù)繼續(xù)執(zhí)行事務(wù)也會(huì)導(dǎo)致死鎖。
解決方式是只能php start.php reload 然後就正常了。

請(qǐng)問(wèn)有遇過(guò)類似情況的嗎?

問(wèn)題已經(jīng)解決,感謝各位。
順帶寫清楚原因方便後者參考。

$dbCheck = $request->post('dbCheck',false);
try{
    Db::beginTransaction();
    $rs = Db::table('tbl_product ')->where('p_id','=',12345)->select('planID')->first();
    if(!$rs) throw new Exception('not found');
    $rs = Db::table('tbl_rateList')->where([
        'planID'=>$rs->planID,
        'bookDate'=>date('Y-m-d')
    ])->select('id,Price')->first();
    if(!$rs){
        Db::table('tbl_rateList')->insert(.....);
    }else{
        Db::table('tbl_rateList'))->where([
            'planID'=>$rs->planID,
            'bookDate'=>date('Y-m-d')
        ])->update(.....);
    }
    if($dbCheck){
        Db::rollBack();
        return json();
    }
    Db::commit();
    return json([....])
}catch(\Exception $e)
{
    Db::rollBack();
    return json([....])
}

$dbCheck這個(gè)是後加的功能,實(shí)際代碼中有價(jià)格核驗(yàn),比底價(jià)低了就進(jìn)行二次確認(rèn)。
在單線程下進(jìn)行,是沒(méi)有問(wèn)題的,但是只要符合這三個(gè)條件,那麼就會(huì)產(chǎn)生死鎖:

1、3個(gè)或以上的INSERT操作
2、表中存在UNIQUE類型的唯一索引
3、第一個(gè)INSERT操作產(chǎn)生了rollBack

還原過(guò)程

前端是進(jìn)行批量修改的時(shí)候,會(huì)傳遞DBConfirm參數(shù),而單條記錄的修改,是不會(huì)傳遞這個(gè)參數(shù)。
如果用戶首先進(jìn)行批量修改,傳遞了dbConfirm參數(shù),同時(shí)其他用戶也進(jìn)行批量/單條記錄的修改,此時(shí)會(huì)產(chǎn)生3個(gè)INSERT的transaction。

在SQL中抽象的流程為:
事務(wù)1 獲取了X鎖,並且插入成功;
事務(wù)2 嘗試插入,檢查UNIQUE索引,獲取S鎖 但失敗,繼續(xù)等待事務(wù)1
事務(wù)3 嘗試插入,檢查UNIQUE索引,獲取S鎖 但失敗,繼續(xù)等待事務(wù)1
當(dāng)事務(wù)1 進(jìn)行回滾,事務(wù)2和事務(wù)3 此時(shí)能獲取S鎖,檢查重複值之後執(zhí)行插入申請(qǐng)X鎖,但由於事務(wù)2和3都已經(jīng)獲取了S鎖,所以導(dǎo)致X鎖獲取失敗兩個(gè)事務(wù)進(jìn)入死鎖狀態(tài)。

解決方式

增加多一個(gè)接口,專門處理價(jià)格檢測(cè),不通過(guò)事務(wù)回滾來(lái)進(jìn)行。

彎路

我也是第一次遇見這個(gè)問(wèn)題,最終還是通過(guò)百度和對(duì)mysql的error log還原了整個(gè)事件。
自己的不足在於太過(guò)先入為主,總覺(jué)得是update導(dǎo)致的死鎖,哪知道是特定環(huán)境下insert導(dǎo)致的死鎖。(從第一次發(fā)生,把一開始通過(guò)UNIQUE KEY進(jìn)行update操作改為 通過(guò)主鍵ID進(jìn)行update操作)包括再這裡提問(wèn),我都是傾向於問(wèn)題存在於 多線程 + UPDATE。

2480 4 1
4個(gè)回答

胡桃

都什么年代,還在用傳統(tǒng)事務(wù)。

  • Mr_Deng 2022-05-30

    老兄都是怎么用的事務(wù)?

胡桃

不用猜都知道你的代碼有問(wèn)題,建議仔細(xì)檢查代碼排查出問(wèn)題,要么改成可串行化。

  • MarkGo 2022-05-30

    runtime/logs 下的都查了,沒(méi)發(fā)現(xiàn)異常;代碼有問(wèn)題不至于隔三差五才死鎖吧?而且代碼異常的話拋出錯(cuò)誤后執(zhí)行的rollback,也不會(huì)導(dǎo)致數(shù)據(jù)庫(kù)死鎖啊?,F(xiàn)在webman是沒(méi)有任何異常,是數(shù)據(jù)庫(kù)產(chǎn)生死鎖。至于mysql的日志也查了,之前是通過(guò)索引進(jìn)行更新記錄,后來(lái)修改為根據(jù)主鍵ID更新記錄。

Tinywan

截圖

  • MarkGo 2022-05-30

    這個(gè)我清楚,所以排查過(guò)SQL語(yǔ)句,我是對(duì)A表進(jìn)行操作。
    偽代碼:

    try{
        Db::beginTransaction();
        $rs = Db::table('tbl_product ')->where('p_id','=',12345)->select('planID')->first();
        if(!$rs) throw new Exception('not found');
        $rs = Db::table('tbl_rateList')->where([
            'planID'=>$rs->planID,
            'bookDate'=>date('Y-m-d')
        ])->select('id')->first();
        if(!$rs){
            Db::table('tbl_rateList')->insert(.....);
        }else{
            Db::table('tbl_rateList'))->where([
                'planID'=>$rs->planID,
                'bookDate'=>date('Y-m-d')
            ])->update(.....);
        }
        Db::commit();
        return json([....])
    }catch(\Exception $e)
    {
        Db::rollBack();
        return json([....])
    }

    但是情況也是一樣。

  • xiuwang 2022-05-30

    異常最好catch (\Throwable $e)不要用catch(\Exception $e)。
    catch \Throwable 可以捕獲任何異常和Error錯(cuò)誤,catch \Exception 只能捕獲異常,不能捕獲Error

  • za2883632 2022-05-30

    就是代碼問(wèn)題。有事務(wù),沒(méi)有手動(dòng)提交!,或者沒(méi)回滾??!