如何让DB::transaction在重试前等待以避免死锁?
给Laravel事务重试添加间隔时间解决死锁问题
Laravel原生的DB::transaction()方法在处理重试时确实没有内置延迟机制,所有重试会立即执行,对死锁场景帮助有限。要快速实现带延迟的重试,你可以自己封装一个事务处理函数,手动控制重试间隔:
封装带延迟重试的事务函数
function transactionWithDelay(callable $callback, int $attempts = 3, int $delayMs = 1000) { $attemptCount = 0; retryLoop: try { // 让原生事务只执行1次,我们自己控制重试逻辑 return DB::transaction($callback, 1); } catch (\Illuminate\Database\QueryException $e) { // 匹配死锁对应的错误码,根据你的数据库调整: // MySQL: 1213, PostgreSQL: 40P01, SQL Server: 1205 $deadlockErrorCodes = [1213, 40P01]; if (in_array($e->getCode(), $deadlockErrorCodes) && ++$attemptCount < $attempts) { // 转换为微秒等待,比如1000ms = 1秒 usleep($delayMs * 1000); goto retryLoop; } // 非死锁异常或重试次数耗尽,抛出原异常 throw $e; } }
使用示例
把你的事务逻辑传入这个函数即可,指定重试次数和间隔时长:
// 重试3次,每次间隔2秒 transactionWithDelay(function () { // 这里放你的导入流程事务逻辑 $this->handleImportStep(); }, 3, 2000);
注意事项
- 错误码适配:不同数据库的死锁错误码不同,一定要根据你使用的数据库调整
$deadlockErrorCodes数组里的值 - 延迟时长:建议设置1-5秒的间隔,具体根据你的业务执行时长调整,确保其他冲突事务有足够时间释放锁
- 临时方案提醒:这个方法只是临时规避死锁的手段,长期来看还是要优化你的迭代逻辑,缩短事务时长、减少锁的范围,从根源解决死锁问题
内容的提问来源于stack exchange,提问作者AlexandrX
相关产品推荐
相关产品推荐

