使用的是 "illuminate/database": "^9.3"
最近业务上线,发现经常产生死锁。
排查过sql语句,并没有发现什么异常
隔离级别是Read committed,
执行update的时候也是根据主键ID进行的。
隔三差五的就会导致mysql死锁,通过KILL把死锁的KILL了也没用,后续继续执行事务也会导致死锁。
解决方式是只能php start.php reload 然後就正常了。
请问有遇过类似情况的吗?
問題已經解決,感謝各位。
順帶寫清楚原因方便後者參考。
$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這個是後加的功能,實際代碼中有價格核驗,比底價低了就進行二次確認。
在單線程下進行,是沒有問題的,但是只要符合這三個條件,那麼就會產生死鎖:
1、3個或以上的INSERT操作
2、表中存在UNIQUE類型的唯一索引
3、第一個INSERT操作產生了rollBack
前端是進行批量修改的時候,會傳遞DBConfirm參數,而單條記錄的修改,是不會傳遞這個參數。
如果用戶首先進行批量修改,傳遞了dbConfirm參數,同時其他用戶也進行批量/單條記錄的修改,此時會產生3個INSERT的transaction。
在SQL中抽象的流程為:
事務1 獲取了X鎖,並且插入成功;
事務2 嘗試插入,檢查UNIQUE索引,獲取S鎖 但失敗,繼續等待事務1
事務3 嘗試插入,檢查UNIQUE索引,獲取S鎖 但失敗,繼續等待事務1
當事務1 進行回滾,事務2和事務3 此時能獲取S鎖,檢查重複值之後執行插入申請X鎖,但由於事務2和3都已經獲取了S鎖,所以導致X鎖獲取失敗兩個事務進入死鎖狀態。
增加多一個接口,專門處理價格檢測,不通過事務回滾來進行。
我也是第一次遇見這個問題,最終還是通過百度和對mysql的error log還原了整個事件。
自己的不足在於太過先入為主,總覺得是update導致的死鎖,哪知道是特定環境下insert導致的死鎖。(從第一次發生,把一開始通過UNIQUE KEY進行update操作改為 通過主鍵ID進行update操作)包括再這裡提問,我都是傾向於問題存在於 多線程 + UPDATE。
都什么年代,还在用传统事务。
老兄都是怎么用的事务?
不用猜都知道你的代码有问题,建议仔细检查代码排查出问题,要么改成可串行化。
runtime/logs 下的都查了,没发现异常;代码有问题不至于隔三差五才死锁吧?而且代码异常的话抛出错误后执行的rollback,也不会导致数据库死锁啊。现在webman是没有任何异常,是数据库产生死锁。至于mysql的日志也查了,之前是通过索引进行更新记录,后来修改为根据主键ID更新记录。
这个我清楚,所以排查过SQL语句,我是对A表进行操作。
伪代码:
但是情況也是一樣。
异常最好catch (\Throwable $e)不要用catch(\Exception $e)。
catch \Throwable 可以捕获任何异常和Error错误,catch \Exception 只能捕获异常,不能捕获Error
就是代码问题。有事务,没有手动提交!,或者没回滚!!
也是死锁,有什么好的办法解决吗
https://www.workerman.net/q/3788 和这个问题差不多吧
一会简体 一会繁体,看的眼睛疼