最直接线索是reserved_at有值但远超retry_after未完成:查jobs表中reserved_at IS NOT NULL且与available_at差值大于retry_after(如设90秒却差15分钟),表明worker崩溃或卡死;需结合Redis实际状态(LLEN queues:default:reserved)及配置一致性排查。
查 jobs 表里 reserved_at 和 available_at 的差值
队列任务卡住最直接的线索就是 reserved_at 有值但没执行完——说明它被某个 worker 拿走了,却迟迟没完成或没释放。先确认是不是真“挂起”:用 phpmyadmin 打开 jobs 表,筛选 reserved_at is not null,再看 reserved_at 和 available_at 的时间差。如果差值远大于你设的 retry_after(比如设了 90 秒,但差了 15 分钟),基本能断定 worker 崩了或卡死在某处。
-
available_at是任务本该可执行的时间(入队时计算) -
reserved_at是 worker 开始处理的时间(由php artisan queue:work更新) - 正常流程中,
reserved_at到deleted_at或released_at应该在retry_after秒内完成
检查 Laravel 队列配置里的 retry_after 和 timeout 是否冲突
常见坑是 retry_after 设太短(如 30 秒),而实际任务耗时超 30 秒,worker 还没跑完就被认为“失败”,自动释放回队列;接着又被抢走、又超时……形成循环挂起假象。更麻烦的是,如果 timeout(进程级超时)比 retry_after 还长,PHP 进程可能卡住不退出,reserved_at 就一直不更新也不清空。
- 查
config/queue.php中对应连接的retry_after(单位秒) - 查
php artisan queue:work --timeout=60启动参数是否和配置匹配 - 数据库字段
retry_after实际生效值以配置为准,不是表结构里的默认值
用 phpMyAdmin 快速识别“幽灵保留”任务
所谓“幽灵保留”,是指 reserved_at 有值,但对应 worker 进程早已退出,也没写 failed_at 或 released_at。这种任务会永远占着坑。在 phpMyAdmin 中执行这个查询:
SELECT id, job, reserved_at, available_at, created_at FROM jobs WHERE reserved_at IS NOT NULL AND (failed_at IS NULL OR failed_at = '0000-00-00 00:00:00') AND reserved_at < NOW() - INTERVAL 5 MINUTE;
把 5 分钟换成你业务能接受的最大处理时长。结果里如果 job 字段是序列化字符串,别点开全量查看——容易卡浏览器;右键复制后本地用 php artisan tinker + unserialize() 解析更稳。
- 不要手动删这些记录,先确认没其他 worker 正在跑
- 避免用 phpMyAdmin 的“编辑”功能改
reserved_at为 NULL——可能触发并发冲突 - 清理优先用命令:
php artisan queue:retry all或按 ID 重试
验证 Redis 驱动下是否因连接复用导致 reserved_at 滞留
如果你用的是 Redis 队列(redis 连接),reserved_at 其实不存数据库,而是存在 Redis 的 queues:default:reserved 列表里。此时 phpMyAdmin 看不到真实状态,jobs 表只是个日志备份。这时候 reserved_at 字段可能根本没更新,或者更新延迟严重。
立即学习“PHP免费学习笔记(深入)”;
- 运行
redis-cli -a yourpass,然后LLEN queues:default:reserved查当前保留数 - 用
LINDEX queues:default:reserved 0看第一个保留任务内容(含超时时间戳) - Redis 模式下,
retry_after控制的是 Redis key 的 TTL,不是数据库字段
真正难排查的,是混合使用 database + redis 驱动、或部署了多套环境却共用一个 Redis 实例——reserved_at 在数据库里看起来正常,实际 Redis 里早乱套了。



















