PHP队列任务处理核心在于选对存储介质、防丢机制和防卡死策略:Redis需设BRPOP超时并捕获异常,MySQL须用SELECT...FOR UPDATE SKIP LOCKED加事务,RabbitMQ必须手动ACK并配置死信队列。
PHP 实现队列任务处理,核心不在于“能不能做”,而在于“选哪种存储介质 + 怎么防丢 + 怎么不卡死”。用错方案,轻则任务堆积、重复执行,重则数据库锁表、Redis 内存爆满。
用
做内存队列只适合单次脚本内调度
它不是为长期运行设计的——进程结束,队列就清空。常见误用是把它当“全局队列”塞进 Web 请求里,结果每次 HTTP 请求都新建一个
,根本没共享。
和
是线程安全的,但仅限当前 PHP 进程内;Web 服务器(如 Apache/FPM)每个请求都是独立进程,无法跨请求传递
适合场景:CLI 脚本中批量处理一组已知任务,比如导出 1000 条记录前先
所有 ID,再循环
执行
别在
或
属性里存
实例——序列化时会丢失内部指针,
可能返回
Redis
/
队列必须设超时且捕获异常
很多人写消费者脚本只写
,看似“一直等”,实则一断网或 Redis 重启,整个进程就卡死不动,supervisor 也拉不起来。
超时参数不能为
,建议设为
(秒),避免永久阻塞;超时后应主动
再重试,防止 CPU 空转
返回
或抛出
时,必须
并记录日志,否则进程静默退出
消息体建议用
,别直接塞对象——反序列化时类不存在会报致命错误
生产者端用
没问题,但高并发下若需优先级,得换
+ 时间戳做有序队列,
就不适用了
MySQL 队列表必须用
这是 MySQL 8.0+ 才支持的语法,老版本只能靠乐观锁或额外状态字段兜底。不用它,多消费者同时
会查到同一条记录,导致任务被重复执行。
PHP 8.5.5
PHP 8.5.5 是 PHP 8.5 分支的维护更新版本。该版本延续了“小步快跑”的迭代逻辑,通过深度错误修复、底层性能微调以及安全加固,旨在为开发者提供一个更健壮、更高效的运行环境。该版本严格遵守语义化版本规范,不包含破坏性变更。
下载
立即学习
“
PHP免费学习笔记(深入)
”;
SQL 必须包在事务里:
如果
影响行数为
,说明已被其他进程抢走,应立即
下一轮查询,别 sleep
字段务必建索引,否则
全表扫描,10 万条记录时消费者轮询延迟可达秒级
别把大字段(如 HTML 模板)直接存在
里——BLOB 类型拖慢整个表的 SELECT 效率
RabbitMQ 消费者必须手动
否则消息会堆积
默认情况下,
接收到消息后,RabbitMQ 就认为已投递成功。如果 PHP 脚本中途崩溃或
,那条消息就永远消失了——除非你显式关掉自动确认。
创建消费者时必须传
,并在业务逻辑执行成功后调用
没
的消息会堆积在 RabbitMQ 管理界面的 “Unacked” 列,超过内存阈值会触发流控,整个队列卡住
死信队列(DLX)要提前声明:消费者
三次后,消息才进 DLX;别指望靠 PHP 层重试——网络抖动时 ACK 失败,消息可能被重复投递
连接中断时,
和
对象失效,必须重建,不能复用旧实例
真正难的不是“怎么把任务塞进去”,而是“怎么确保它只被执行一次、失败可追溯、扩容时不冲突”。Redis 快但丢数据,MySQL 稳但慢,RabbitMQ 强大但运维成本高——选型时得看你的任务丢了会不会被老板打电话骂醒。
SplQueueSplQueueenqueue()dequeue()enqueuedequeue$_SESSIONstaticSplQueuedequeuenullLPUSHBRPOP$redis->brPop(['task_queue'], 0)010sleep(1)brPopfalseRedisExceptiontry/catchjson_encode(['job' => 'send_email', 'data' => [...]])lPushzAddBRPOPSELECT ... FOR UPDATE SKIP LOCKEDSELECT status = 0BEGIN; SELECT id FROM queue WHERE status = 0 ORDER BY id LIMIT 1 FOR UPDATE SKIP LOCKED; UPDATE queue SET status = 1 WHERE id = ?; COMMIT;UPDATE0continuestatusWHERE status = 0payloadackbasic_consumeexit['no_ack' => false]$channel->basic_ack($msg->getDeliveryTag())ackbasic_nack$channel$connection