Laravel队列失败任务应自动归档至自定义表并压缩存储:禁用默认failed_jobs,用Queue::failing()监听,gzdeflate压缩payload/exception存入archived_failed_jobs(mediumblob字段),冗余exception_class等可检索字段,再分批清理原始表。

队列任务失败后怎么自动存到归档表而不是直接删掉
默认 failed_jobs 表只存失败记录,不压缩、不归档,时间一长占空间还拖慢查询。Laravel 本身不提供“自动归档+压缩”能力,得自己接管失败处理流程。
核心做法是:禁用默认失败存储,改用自定义 fail() 方法把数据序列化后写入新表(比如 archived_failed_jobs),同时删掉原始记录。
- 在
App\Providers\AppServiceProvider的boot()里调用Queue::failing()监听失败事件 - 监听函数中用
$event->job->getRawBody()拿到原始 payload,避免反序列化失败导致丢失数据 - 把
payload、exception、failed_at存进新表,字段类型建议用longtext(别用json类型,兼容旧版本 MySQL) - 务必在存档成功后再调用
$event->job->delete(),否则可能重复归档
归档数据怎么压缩再存,省空间又不影响查原因
直接存原始 payload 和堆栈,一条失败任务轻松占几百 KB;用 gzdeflate() 压缩后通常能压到 1/5 大小,且 PHP 解压毫秒级,查问题时完全无感。
- 存档前对
payload和exception字段分别做gzdeflate(),不要用gzcompress()(它加了 header,解压要配对函数) - 数据库字段用
mediumblob或longblob,别用text,否则压缩后二进制内容会乱码或截断 - 查归档时用
gzinflate($row->payload)还原,注意加try/catch——万一某条损坏,至少不影响其他条查看 - 别对整个 JSON 字符串压缩后再 JSON 编码,那是套娃,徒增开销
怎么定期清理原始 failed\_jobs 表但不丢数据
归档不是目的,目的是让 failed_jobs 表体积可控。Laravel 自带的 queue:flush 是清空,不能选条件;得自己写命令。
- 新建 Artisan 命令
php artisan queue:archive-and-clear,逻辑分两步:先查出failed_at 的 ID 列表,再批量归档、再批量删除 - 别用
where('failed_at', 'delete()直删,容易锁表;用 ID 分批(比如每次 100 条)更安全 - 在
config/queue.php的failed配置里关掉默认数据库驱动:'database' => null,否则queue:work启动时还会建表、写日志 - 归档命令加到 cron,每天凌晨跑一次,比手动强
压缩归档后怎么快速定位哪条任务出了啥错
压缩后不能直接 grep,但也不用全解压查。关键是在归档表里冗余几个可检索字段。
- 额外加
exception_class(如"Illuminate\Queue\MaxAttemptsExceededException")、job_name(从 payload 提取class)、connection字段,都建索引 - 用
DB::raw("CONVERT(SUBSTRING(payload, 1, 200) USING utf8mb4)")截取 payload 前 200 字节存为payload_preview,方便模糊查参数 - 别依赖日志时间戳查问题——归档时刻和失败时刻有延迟,优先用
failed_at字段 - 如果用了 Horizon,记得在
horizon.php里也关掉它的失败监控,否则它自己又写一份到 Redis
归档压缩这事,难点不在压缩算法,而在 payload 结构不稳定、异常对象不可序列化、以及跨版本 Laravel 的 job 序列化格式变化。上线前一定拿真实失败任务测几轮解压还原是否完整。











