Redis的LPUSH/BRPOP可实现轻量队列,适合允许少量丢失、无需事务回滚、单机或小集群场景;数组和SplQueue因内存级、无跨进程持久性、无锁及高并发风险等缺陷不适用。

直接用 Redis 的 LPUSH/BRPOP 就能跑起来,不需要装 RabbitMQ 或改数据库结构——前提是你的任务允许少量丢失、不依赖事务回滚,且单机或小集群够用。
为什么不用数组或 SplQueue 做队列
数组([])和 SplQueue 是内存级数据结构,PHP 请求结束就销毁。你无法让一个请求「入队」、另一个 CLI 进程「出队」;它们只适合单次脚本内流转,比如临时缓存一批待处理 ID。真实队列必须跨进程、跨请求持久存在。
-
array_push()+array_shift()在高并发下会竞争写入,无锁机制,数据错乱风险高 -
SplQueue没有网络协议支持,不能被多个 PHP 实例共享 - 两者都无法支撑守护进程长期运行时的可靠性(如消费者崩溃后任务不丢失)
用 Redis 实现队列的最小可行代码
核心就是两行命令:LPUSH 入队,BRPOP 阻塞出队。别碰 PUB/SUB——它不保证消息可达,也不存历史,不是队列。
生产者(比如用户下单后触发):
立即学习“PHP免费学习笔记(深入)”;
$redis = new Predis\Client();
$task = ['action' => 'send_welcome_email', 'user_id' => 123];
$redis->lPush('queue:email', json_encode($task));
消费者(常驻 CLI 脚本):
while (true) {
// 阻塞最多 30 秒,避免空轮询打满 CPU
$res = $redis->bRPop(['queue:email'], 30);
if ($res) {
$data = json_decode($res[1], true);
try {
processEmail($data); // 你的业务逻辑
} catch (\Exception $e) {
// 记日志,但别 throw —— 否则任务永远卡住
error_log("Failed to process email task: " . $e->getMessage());
}
}
}
- 用
bRPop不用RPop:前者阻塞等待,后者轮询查,浪费连接和 CPU - 键名加前缀(如
queue:email):避免和其他业务 key 冲突 - 不要在
catch里throw或exit:消费者进程必须持续运行,否则队列停摆
容易被忽略的三个落地细节
很多人写完上面代码就以为成了,结果上线后出问题——卡在以下三点:
-
BRPOP超时时间设太短(如 1 秒):频繁唤醒进程,IO 上升,延迟反而变高;建议 10–30 秒 - 没做消费者进程守护:用
nohup php worker.php &启动后,终端关闭或 SSH 断连会导致进程退出;必须用supervisor或systemd管理生命周期 - 没处理「消费者崩溃但任务已取出」的情况:
BRPOP取出即删除,如果后续逻辑崩了,任务就丢了。真要保不丢,得换RPOPLPUSH+ 死信队列方案,或者直接上RabbitMQ的 ACK 机制
如果你的场景是发验证码、站内信这类低敏感任务,Redis 队列足够轻快;一旦涉及支付回调、订单状态同步,就得考虑 MySQL 持久化队列或 RabbitMQ 的确认机制——简单不等于万能,关键看任务丢不起还是丢得起。



















