直接给答案:Laravel 11 起,官方把 .env 里的默认队列连接从 sync 换成了 database。这不是给新手练手的过渡方案,而是明确的态度——绝大多数项目,一张 jobs 表就够了。什么时候才需要 Redis?出现三个信号之一:任务要求毫秒级拾取延迟、需要 Horizon 实时监控、database 驱动真的撑不住吞吐。下面把原理、数字和判断标准一次讲清。

两个驱动的工作原理差在哪

database:一张 jobs 表加事务锁

database 驱动把任务序列化后存进 jobs 表,核心字段就这几个:

id / queue / payload / attempts / reserved_at / available_at / created_at

worker 每轮循环做的事:开一个事务,查出 available_at <= now() 且未被占用的任务,用行锁标记 reserved_at,提交后执行。任务本身跑得并不慢,真正的延迟来源是空闲行为——队列空的时候 worker 会休眠(--sleep=3,默认 3 秒),所以最坏情况下,一个刚投递的任务要等 3 秒才被拾取。

很多人误以为是 MySQL 撑不住队列,其实是这 3 秒的休眠在起作用,跟数据库性能没关系。

Redis:阻塞 pop 加 Lua 原子操作

Redis 驱动用 list 存待处理任务,worker 执行的是阻塞式 pop(block_for 控制),连接挂在 Redis 上等任务上门,任务一到毫秒级就被取走;弹出、延迟、重试全部用 Lua 脚本在 Redis 端原子完成,不走应用层事务。

所以两者的核心差异可以一句话概括:database 驱动是轮询模型,Redis 驱动是等待模型。延迟差距主要来自这里,吞吐差距则来自每任务一次 MySQL 事务提交的开销。

能力对比:差距比你想的小

能力 database Redis
延迟任务 delay 支持 支持
失败重试 backoff 支持 支持
任务批次 Bus batch 支持 支持
速率限制 走 cache 锁 原生 throttle,更精准
多队列优先级 多队列名加多 worker 同左,切换成本更低
空闲拾取延迟 最坏 3 秒(sleep 可调) 毫秒级(阻塞 pop)
监控面板 无 Horizon
额外依赖 无,复用现有 MySQL 需要一个 Redis 实例
故障时任务可读性 直接 SQL 查 jobs 表 得用 redis-cli

注意两点。第一,Laravel 10 重写过 database 驱动,锁竞争和查询实现都优化过,早年间数据库队列又慢又容易锁死的印象该更新了。第二,功能清单上两者几乎打平,真正的差距集中在三处:拾取延迟、吞吐上限、可观测性。

吞吐实测参考

2 核 4G 虚拟机,MySQL 8 和 Redis 7 同机部署,跑空 payload 任务的结果:

场景 database 驱动 Redis 驱动
单 worker 吞吐 约 350 jobs/s 约 1800 jobs/s
4 worker 吞吐 约 1200 jobs/s(锁竞争出现) 近线性扩展
空闲时任务拾取延迟 最坏 3 秒 毫秒级

单看数字差 5 倍,换算一下就没那么吓人:350 jobs/s 意味着理论上日均 3000 万任务的消化能力。而多数 PHP 业务系统一天的真实异步任务是几千到几十万个,算下来平均每秒不到 10 个,连 database 驱动一成的功力都用不到。

该换 Redis 的三个信号

信号一,延迟敏感。 有些任务必须毫秒级被拾取,比如实时通知、支付回调后的状态机流转。database 驱动把 --sleep 调到 1 秒可以改善,但不能设 0——0 会让 worker 空转,每秒几十次空查询直接打爆 CPU 和 MySQL。

信号二,需要可观测性。 Horizon 只支持 Redis 驱动,它给的东西(吞吐图表、失败告警、任务重放、supervisor 管理)在团队规模上来之后比性能更早变成刚需。一个人写项目无所谓,五个人以上运维异步任务,没有面板的日子会很难受。

信号三,吞吐真不够。 两种表现:jobs 表写入已经拖慢主库(它和业务表同库),或者 worker 数加到 CPU 上限任务仍然堆积。到这一步再换,而且优先确认是不是任务本身该异步拆分。

用 database 驱动的四个注意事项

  • 空闲延迟:默认 --sleep=3。对邮件、报表导出、数据同步这类任务完全无感;在意的话调到 1 秒。
  • reserved 状态卡任务:worker 崩溃时任务会停留在 reserved 状态,要等 retry_after(默认 90 秒)才释放。这个值必须大于最长任务的执行时间,否则同一个任务会被两个 worker 重复执行。
  • 记得 queue:restart:部署新代码后重启 worker,别让它带着旧代码继续跑。
  • failed_jobs 表膨胀:失败任务全部落表,记得定期清理或归档,长年累月会积累得比 jobs 表还大。

真要迁移,清单如下

  • QUEUE_CONNECTION=redis,确认 config/queue.php 里 redis 连接可用
  • 给 Redis 开 AOF 持久化,否则 Redis 一重启,队列里的任务直接消失——这是 database 驱动反而不怕的问题
  • composer require laravel/horizon,配置好 supervisor 和告警
  • 切换前把 database 队列里堆积的任务跑完,跑不完就手动迁移,别硬切

FAQ

Q:jobs 表和业务表同一个库,会互相拖累吗? 任务量小的阶段影响可以忽略;真到了有影响的量级,可以把队列连到独立的 MySQL 实例,不必非换 Redis。

Q:我已经用 Redis 做缓存了,队列顺手也换过去? 没必要绑定这两个决策。缓存用 Redis 是为了性能,队列驱动看的是延迟、监控、吞吐三个信号,按信号判断,不按手头有什么组件判断。

Q:Redis 驱动会丢任务吗? 默认会——Redis 是内存存储,重启即丢。开了 AOF 才安全;而 database 驱动天然落盘,可靠性上反而是它占优。所以别把换 Redis 当成无脑升级,它是一次有得有失的迁移。

艾林博客 - 技术分享、开发经验与AI探索的个人技术博客
艾林博客 - 技术分享、开发经验与AI探索的个人技术博客

延伸阅读:

GPT-6 Sol 和 Luna 发布:价格砍到 Astra 的 1%,但按价签选模型会踩坑 行业快讯
GPT-6 Sol 和 Luna 发布:价格砍到 Astra 的 1%,但按价签选模型会踩坑

9 月 22 日 OpenAI 补上 GPT-6 的 Sol 和 Luna 两档,价格降到 Astra 的 1/5 和 1/100。但重点不是便宜:三档能力差距按工作类型分叉——编码任务上 Luna 只落后 Sol 2.2 分,智能体任务上却掉 12.5 分。本文附三档规格对比、计费隐藏条款、GPT-6 Sol 与前代的代际对比,以及一段可直接抄走的 PHP 模型路由实现。

AI 后端

Valencio

/

2026-09-23