Skip to main content

概述

Laravel 的队列功能提供了两种执行控制:任务的去重(Unique)防抖(Debounce)。两者都是为了在同一任务被多次派发时避免无谓的执行,但行为有所不同。
Unique Jobs 与 Debounced Jobs 互斥。请不要在使用 DebounceFor 属性的任务上实现 ShouldBeUnique

Unique Jobs — ShouldBeUnique

只要同一任务存在于队列中,其他派发都会被忽略。
UpdateSearchIndex 位于队列中(或正在处理中)时,尝试派发同一任务会被忽略。

通过键细化唯一约束 — UniqueFor + uniqueId()

即使是同一任务类,若希望将”更新商品 A”和”更新商品 B”视为不同任务,可通过 uniqueId() 方法定义键。
  • uniqueId() 返回的值将作为缓存锁的键。
  • 指定 #[UniqueFor(秒数)] 后,经过该秒数后锁将自动释放(当任务未被处理时的故障保护)。

指定缓存驱动 — uniqueVia()

若希望使用默认缓存驱动以外的驱动,可实现 uniqueVia()
Unique Jobs 需要支持原子锁的缓存驱动(redisdatabasememcacheddynamodbfilearray)。

ShouldBeUnique vs ShouldBeUniqueUntilProcessing

ShouldBeUnique 的锁会保持到任务完成或达到重试上限。这在某些场景下会成为问题。 示例: 队列中有一个 UpdateSearchIndex(product_id: 42),希望在 Worker 开始处理后立即再次派发同一任务。使用 ShouldBeUnique 时,在处理完成前第二个任务不会入队。 此时应使用 ShouldBeUniqueUntilProcessing。由于锁在处理开始前就被释放,因此 Worker 取出任务的瞬间即可派发下一个任务。

对比总结


Debounced Jobs — #[DebounceFor]

DebounceFor 属性是 Laravel 13 新增的功能。
当同一任务在短时间内被大量派发时,仅执行最后一次派发的任务。这与 Web 前端的防抖思路相同。
  • debounceId() 返回值来识别任务(每个商品 ID 会应用独立的防抖)。
  • 即使 30 秒内以相同 productId 派发 10 次,也只会执行最后一次。

maxWait — 最大等待时间上限

对于频繁更新的数据,防抖可能会不断持续从而导致任务永远无法执行。可以通过 maxWait 设置最大延迟时间。
在此示例中,从首次派发算起,最迟 120 秒后必定会执行(即使 30 秒防抖持续也会在 120 秒时超时执行)。

指定缓存驱动 — debounceVia()

JobDebounced 事件

被后续派发覆盖的任务会派发 Illuminate\Queue\Events\JobDebounced 事件并从队列中删除。通过监听该事件,可以对被防抖的任务进行跟踪和监控。

应该选择哪一个


内部实现

Unique Jobs 的锁机制

当派发 ShouldBeUnique 任务时,Laravel 会在内部获取缓存的原子锁。锁键的格式如下:
如果无法获取锁(已被其他任务持有),任务不会被添加到队列中。

Debounced Jobs 的实现

DebounceFor 内部使用管理”防抖窗口”的缓存条目。每当新的派发到来时:
  1. 从队列中删除已存在的任务(派发 JobDebounced 事件)
  2. 将新任务添加到队列(带有防抖秒数的延迟)
  3. 重置缓存中的计时器
若指定了 maxWait,也会记录首次派发的时间戳,并防止从该时刻起防抖超过 maxWait 秒。

参考链接

最后修改于 2026年8月2日