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