Redis存进度比数据库更靠谱,因其INCR/SET原子操作、低延迟及过期机制适配高频更新;须用dispatch()而非dispatchNow()、前端指数退避轮询、任务完成删键防残留。

用 Redis 存进度比数据库更靠谱
队列任务跑几分钟甚至几小时,中间不能只靠“开始/结束”两个状态。用数据库每秒更新一次 progress 字段,容易锁表、拖慢主业务;而 Redis 的 INCR、SET 和过期时间天然适合高频写入和临时状态存储。
- 任务启动时,用
Redis::set("job:{$jobId}:progress", 0)初始化,并设EX 3600防止脏数据堆积 - 关键处理节点调用
Redis::incr("job:{$jobId}:progress"),比SET+ 计算更原子 - 避免用
cache()->put(),它可能走文件或数据库驱动,不保证原子性和低延迟 - 前端轮询时直接读
Redis::get("job:{$jobId}:progress"),别绕一圈查数据库
dispatchNow() 和 dispatch() 对进度追踪的影响
同步执行(dispatchNow())看似方便,但会阻塞当前请求线程,前端无法轮询进度——因为任务还没进队列,Redis 键根本没机会被创建或更新。
- 必须用
dispatch()把任务推到队列,哪怕本地用sync驱动也要走完整流程 - 在
__construct()或handle()开头立刻写入初始进度,否则刚 dispatch 就轮询可能返回null - 如果测试时发现进度始终为 0,先检查是否误用了
dispatchNow(),再确认Redis连接配置是否和队列 worker 一致
前端轮询时怎么防雪崩和误判
100 个用户同时盯着同一个长任务,每秒发请求,后端压力不小,而且容易因网络抖动或缓存拿到旧值。
Laravel Cloud(cloud.laravel.com)。此技能用于处理所有 Laravel Cloud 请求,包括搜索和读取数据。只要任务涉及 Laravel Cloud,即使用此技能。
- 前端用指数退避:从 1s 轮询起步,每次失败或未完成就翻倍,上限设为 30s
- 后端响应里加
X-Job-Status: running或completed头,不单靠数字判断是否结束 - 任务完成后主动删掉
Redis进度键:Redis::del("job:{$jobId}:progress"),避免下次同 ID 任务读到残留值 - 不要用
job_id做 URL 参数传给前端,而是返回一个一次性token,服务端映射到真实 job ID,防遍历和重放
大文件分块处理时的进度校准问题
比如导出 50 万行 Excel,按每 5000 行一个 chunk 处理,总进度不是简单除法——有些 chunk 可能失败重试,有些合并逻辑耗时差异大。
- 把“总工作量”写死在任务构造时:
$this->total = ceil($totalRows / 5000),存在Redis的另一个 key 里 - 每次成功处理完一个 chunk,用
Redis::incr("job:{$jobId}:done"),再用GET两个 key 算百分比,避免浮点误差 - 如果某 chunk 报错并重试,别重复
INCR,改用Redis::get("job:{$jobId}:done")校验后再决定是否更新 - 注意
Excel写入本身不触发进度更新,得在PhpSpreadsheetsave()完成后再写一次 100%
进度键名拼接、过期时间、前后端状态对齐,这三处稍不注意,就会出现“明明完成了还卡在 99%”或者“刷新页面进度归零”的情况。










