不能只用一个redis队列,因订单、日志、统计任务混杂导致资源争抢与延迟失控;需用多list分级:queue:high(强一致,短超时)、queue:mid(秒级延迟,brpop带超时)、queue:low(后台轮询);worker数须匹配db连接池,任务体须精简,杜绝消费者端冗余计算。

为什么不能只用一个Redis队列?
当订单、日志、统计三类任务全塞进同一个 queue:default,消费者 Worker 一拉就是混合任务:刚处理完一条耗时 200ms 的库存扣减,下一条却是 5ms 的短信发送。这导致数据库连接长时间被高优先级任务占用,低延迟任务排队等待,而写入压力却集中在同一时刻爆发——本质是任务粒度和资源调度不匹配。
用 Redis 多 List 实现分级:关键在命名与消费策略
分级不是靠复杂中间件,而是靠明确的队列命名 + 独立 Worker 进程 + 差异化消费逻辑:
-
queue:high:存放库存扣减、支付状态更新等强一致性任务,Worker 启动时设置$redis->setOption(Redis::OPT_READ_TIMEOUT, 0.1)缩短超时,避免阻塞 -
queue:mid:邮件、短信、通知类任务,可接受秒级延迟,Worker 使用BRPOP queue:mid 2带超时弹出,空闲时主动让出 CPU -
queue:low:日志归档、报表生成等后台任务,用LPUSH+RPOP非阻塞轮询,每 5 秒查一次,避免抢占资源
注意:BRPOP 是阻塞命令,但不同队列用不同 timeout 值,就能控制各等级任务的响应敏感度;不要在 queue:high 上用长 timeout,否则会掩盖数据库慢查询问题。
Worker 进程数与数据库连接池必须对齐
分级失效的常见原因是“Worker 数 > 数据库连接数”,比如开了 8 个 queue:high Worker,但 MySQL 最大连接数只有 10,其中 2 个还被 Web 请求占着——结果所有高优任务卡在连接等待上,分级变成空谈。
- 每个 Worker 进程在
onWorkerStart中建立唯一 DB 连接,复用至进程生命周期结束 -
queue:highWorker 数 ≤ (MySQLmax_connections- Web 服务预留连接数) / 1.5(留缓冲) - 用
SHOW PROCESSLIST观察实际活跃连接,而非只看配置值
分级后仍出现写入抖动?检查任务体是否携带冗余数据
一个典型坑:把整张订单数组 JSON 序列化后扔进 queue:high,结果 Worker 消费时还要反序列化、再查一遍用户信息、再校验地址——这些本该在生产者端完成的计算,全堆到数据库连接持有期间做,放大了锁等待时间。
- 生产者推送前,只保留数据库写入必需字段(如
order_id,stock_delta,updated_at) - 禁止在消费者中执行
SELECT ... FOR UPDATE以外的任何跨表 JOIN 或聚合查询 - 对
queue:high任务加字段校验,例如收到无order_id的消息直接丢弃并告警,防止脏数据拖垮整个队列
分级队列真正起效的前提,是任务体足够轻、DB 连接数够用、且没有把“业务逻辑下沉”误当成“解耦”。这三个点里漏掉任何一个,压力都会绕过队列,原样砸在数据库上。











