You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何防止Laravel中相同user_id的队列Job并发运行,实现4秒间隔执行

Laravel队列同user_id任务并发执行问题解决方案

问题根因

  1. 锁驱动不兼容:如果使用database、file等非Redis驱动,Laravel的原子锁无法保证多进程下的互斥性,会出现锁击穿导致任务并发执行
  2. 锁过期时间配置过短:你配置的$uniqueFor和WithoutOverlapping的expireAfter均为4秒,若任务执行时长超过预期、或队列有积压导致任务排队时间过长,锁会提前释放,下一个任务会提前获取到锁
  3. Unique机制作用范围限制:ShouldBeUnique仅能防止任务分发阶段重复入队,已经进入队列的多个同user_id任务无法被该机制拦截,积压时多个任务会依次触发锁争抢,容易出现并发

解决方案

  • 统一锁与队列驱动为Redis:修改config/cache.php默认缓存驱动为redis,config/queue.php默认队列驱动为redis,保证原子锁的互斥性
  • 调大锁过期时间:根据任务最大执行时长调整锁过期时间,如任务最长执行1秒,可将$uniqueFor改为10秒,WithoutOverlapping的expireAfter改为6秒,避免锁提前过期:
// 任务类内修改
public $uniqueFor = 10;

public function middleware()
{
    return [(new WithoutOverlapping($this->user_id))->releaseAfter(4)->expireAfter(6)];
}
  • 新增handle层二次锁校验:在任务执行逻辑最开头新增手动原子锁校验,未获取到锁直接延迟4秒放回队列,执行完成后手动释放锁,同时新增最后执行时间校验,严格保证两次执行间隔不低于4秒,避免依赖框架中间件的锁逻辑疏漏:
use Illuminate\Support\Facades\Cache;

public function handle()
{
    // 先校验执行间隔
    $lastExecKey = 'user_action_last_exec_'.$this->user_id;
    $gap = time() - Cache::get($lastExecKey, 0);
    if ($gap < 4) {
        return $this->release(4 - $gap);
    }

    // 手动加锁保证互斥
    $lock = Cache::lock('user_action_running_'.$this->user_id, 6);
    if (!$lock->get()) {
        return $this->release(4);
    }

    try {
        Log::debug('');
        Log::debug('Started Time: '.date('Y-m-d H:i:s'));
        // 原有业务逻辑

        // 执行完成更新最后执行时间
        Cache::put($lastExecKey, time(), 10);
    } catch (Exception $e) {
        Log::critical('Error occurred.');
        Log::critical($e);
        $this->job->fail($e);
    } finally {
        $lock->release();
    }
}
  • 优化队列消费速度:如果available_at与执行时间差值过大,说明队列worker数量不足,可适当增加worker进程数,避免任务长时间积压导致锁逻辑失效

内容的提问来源于stack exchange,提问作者mehri abbasi

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.09.29 16:57:02