sync驱动在高并发下会卡死,因其同步执行所有任务,阻塞HTTP请求线程,导致响应延迟激增、超时、504及重复提交;生产环境必须切换为Redis等异步驱动并正确配置worker参数。

为什么默认用 sync 驱动在高并发下会卡死
因为 sync 驱动是同步执行,所有任务都在当前 HTTP 请求线程里跑完才返回响应。用户发个帖子,你后台要发邮件、推通知、更新统计——全堆在 2 秒内完成,QPS 上去直接拖垮 PHP-FPM 进程。
真实场景中,哪怕只是 50 人同时点击「提交订单」,sync 就会让接口平均响应时间从 200ms 拉到 3s+,超时、504、用户反复点提交导致重复下单全都会发生。
-
QUEUE_CONNECTION=sync只适合本地开发调试,千万别上生产 - 切换前务必确认 Redis 服务已启动且可连:
redis-cli -h 127.0.0.1 -p 6379 ping返回PONG -
.env中必须显式设为QUEUE_CONNECTION=redis,否则 Laravel 仍可能 fallback 到sync
queue:work 启动参数怎么配才不丢任务也不卡死
直接跑 php artisan queue:work 默认用 default 队列、无超时、无限重试——线上等于埋雷。worker 崩了没人拉,任务卡住不释放,Redis 内存涨到爆。
关键参数组合必须带齐:
-
--timeout=90:单个任务最长执行 90 秒,超过就 kill,防止某条慢 SQL 或第三方 API 挂住整个 worker -
--max-jobs=500:每处理 500 个任务后自动重启进程,避免 Laravel 的静态变量/ORM 缓存累积导致内存泄漏 -
--tries=3:失败任务最多重试 3 次,再失败就进failed_jobs表,别让它无限循环 -
--sleep=3:空闲时每 3 秒轮询一次,太短(如 0.1)会高频打 Redis,太长(如 10)会导致新任务积压延迟
正确启动命令:php artisan queue:work --queue=default --timeout=90 --max-jobs=500 --tries=3 --sleep=3
Redis 队列里任务堆积了,怎么快速定位是哪类任务拖慢整体
不是所有任务都该塞进同一个 default 队列。比如发短信和生成 PDF 报表,耗时差 10 倍,混在一起必然互相阻塞。
Redis 缓存和数据结构管理技能。通过自然语言操作 Redis,支持 String、Hash、List、Set、ZSet、Stream 等数据结构操作。当用户提到 Redis、缓存、消息队列、会话存储时使用此技能。
按业务优先级拆队列,再配合 Supervisor 分配不同 worker:
- 建三个队列:
high(短信、支付回调)、default(邮件、日志)、low(数据归档) - 任务分发时指定:
SendSms::dispatch($phone)->onQueue('high') - Supervisor 配置里分开管理:
command=php /app/artisan queue:work --queue=high单独起 2 个进程,default起 4 个,low起 1 个
查堆积量用 Redis 命令:redis-cli llen 'queues:high'、redis-cli llen 'queues:default' —— 哪个数字持续上涨,就重点查对应任务的 handle() 逻辑有没有阻塞 IO 或没加索引的 DB 查询。
为什么用 lpop + llen 做库存扣减会超卖
这是最典型的并发陷阱。代码写成先 llen 查长度,再 lpop 扣减,中间没锁 —— 100 个请求同时看到「还有 1 库存」,结果全扣成功。
Redis 的原子性只在单命令内有效。llen 和 lpop 是两个独立操作,必须合并成一个原子动作:
- 正确做法:用
lpop直接取,返回null就说明没库存了,不用提前查 - 或者用 Lua 脚本封装判断 + 扣减逻辑,保证整个流程不可中断
- Laravel 自带的
Redis::transaction()在队列场景下无效,它只对当前连接生效,而 worker 是多进程的
真正防超卖,靠的是 Redis 列表的 lpop 本身原子性,而不是你在 PHP 里写的 if-else。

















