概述
Laravel 的队列功能提供了两种执行控制:任务的去重(Unique)和防抖(Debounce)。两者都是为了在同一任务被多次派发时避免无谓的执行,但行为有所不同。Unique Jobs — ShouldBeUnique
只要同一任务存在于队列中,其他派发都会被忽略。
UpdateSearchIndex 位于队列中(或正在处理中)时,尝试派发同一任务会被忽略。
通过键细化唯一约束 — UniqueFor + uniqueId()
即使是同一任务类,若希望将”更新商品 A”和”更新商品 B”视为不同任务,可通过 uniqueId() 方法定义键。
uniqueId()返回的值将作为缓存锁的键。- 指定
#[UniqueFor(秒数)]后,经过该秒数后锁将自动释放(当任务未被处理时的故障保护)。
指定缓存驱动 — uniqueVia()
若希望使用默认缓存驱动以外的驱动,可实现 uniqueVia()。
Unique Jobs 需要支持原子锁的缓存驱动(
redis、database、memcached、dynamodb、file、array)。ShouldBeUnique vs ShouldBeUniqueUntilProcessing
ShouldBeUnique 的锁会保持到任务完成或达到重试上限。这在某些场景下会成为问题。
示例: 队列中有一个 UpdateSearchIndex(product_id: 42),希望在 Worker 开始处理后立即再次派发同一任务。使用 ShouldBeUnique 时,在处理完成前第二个任务不会入队。
此时应使用 ShouldBeUniqueUntilProcessing。由于锁在处理开始前就被释放,因此 Worker 取出任务的瞬间即可派发下一个任务。
对比总结
Debounced Jobs — #[DebounceFor]
DebounceFor 属性是 Laravel 13 新增的功能。- 由
debounceId()返回值来识别任务(每个商品 ID 会应用独立的防抖)。 - 即使 30 秒内以相同
productId派发 10 次,也只会执行最后一次。
maxWait — 最大等待时间上限
对于频繁更新的数据,防抖可能会不断持续从而导致任务永远无法执行。可以通过 maxWait 设置最大延迟时间。
指定缓存驱动 — debounceVia()
JobDebounced 事件
被后续派发覆盖的任务会派发 Illuminate\Queue\Events\JobDebounced 事件并从队列中删除。通过监听该事件,可以对被防抖的任务进行跟踪和监控。
应该选择哪一个
内部实现
Unique Jobs 的锁机制
当派发ShouldBeUnique 任务时,Laravel 会在内部获取缓存的原子锁。锁键的格式如下:
Debounced Jobs 的实现
DebounceFor 内部使用管理”防抖窗口”的缓存条目。每当新的派发到来时:
- 从队列中删除已存在的任务(派发
JobDebounced事件) - 将新任务添加到队列(带有防抖秒数的延迟)
- 重置缓存中的计时器
maxWait,也会记录首次派发的时间戳,并防止从该时刻起防抖超过 maxWait 秒。