Laravel 8队列任务超时,清缓存重启Horizon可临时修复,寻根因及解决方法
可能的触发诱因
- Redis内存配置不合理:如果你的Redis没有设置合理的
maxmemory和内存淘汰策略,默认的noeviction策略会在内存占满时直接拒绝所有写入请求。event sourcing架构产生的大量事件相关缓存如果未设置过期时间,刚好2-3个月的累积量就能打满Redis内存,导致队列任务读写缓存失败,触发超时、重试超限的异常。执行cache:clear会清空所有缓存释放内存,自然恢复正常。 - 缓存键无限膨胀:事件驱动场景下会按事件ID、聚合根ID生成大量唯一缓存键,如果没有设置过期时间,键的数量会随时间线性增长,Redis的查询性能会随键数上升持续下降,高吞吐队列场景下就会出现大量请求超时。
- 缓存锁异常残留:如果事件处理逻辑中用到了Laravel缓存锁,未设置锁过期时间或者进程异常退出时没有正确释放锁,日积月累残留的锁会越来越多,后续任务拿不到锁就会反复重试,最终触发尝试次数过多的报错,清缓存时会把所有残留锁一并删除,问题就临时解决。
- 队列worker内存泄漏:常驻的队列worker进程如果在事件处理逻辑中存在内存泄漏(比如静态变量、全局数组无限追加数据未释放),连续运行2-3个月后内存占用过高,触发系统swap或者OOM,导致任务处理速度大幅下降触发超时。部署时除了执行
cache:clear也会重启队列worker,也可能是恢复的原因之一。
排查步骤
- 首先验证根因范围:下次出现问题时不要立刻全量部署,先单独手动执行
php artisan cache:clear,如果问题立刻恢复,可以确认和缓存直接相关;如果执行后未恢复,再手动重启队列worker验证是否是worker进程的问题。 - 排查Redis运行状态:执行
redis-cli info memory查看核心指标:确认maxmemory设置值、used_memory当前使用量、maxmemory_policy淘汰策略,同时用redis-cli dbsize查看当前缓存键总数,对比正常时期的数值,确认是否是内存或键数达到阈值后触发的问题。 - 检查缓存键过期策略:用
redis-cli --scan抽查业务相关的缓存键,执行ttl 键名查看是否设置了过期时间,统计永久有效键的占比,重点排查事件、聚合根相关的缓存键是否存在永久有效的情况。 - 排查Redis慢查询:执行
redis-cli slowlog get 100查看慢日志,确认出问题时段是否有大量慢查询,是否和队列任务用到的缓存操作相关。 - 监控队列worker内存:日常监控
queue:work进程的内存占用,确认是否随运行时间持续上涨,出问题时进程内存是否已经达到设置的上限或者服务器内存阈值。
彻底修复方案
- 优化Redis配置:根据业务数据量设置合理的
maxmemory,将内存淘汰策略修改为allkeys-lru(优先淘汰最少使用的键)或者volatile-lru(仅淘汰设置了过期时间的最少使用键),避免内存占满时拒绝写入的问题,有条件的话迁移到独立Redis实例,避免和应用、队列进程争抢资源。 - 补全全量缓存过期规则:所有业务缓存键必须设置合理的过期时间,事件类临时缓存建议设置1-24小时的过期时间,聚合根快照类缓存根据业务频率设置7-30天的过期时间,绝对禁止无特殊需求的永久缓存。
- 优化队列worker稳定性:启动队列worker时增加
--memory=128参数(可根据业务调整内存阈值),进程内存超过阈值时自动重启,避免内存泄漏累积;同时配置每周定时重启所有队列worker的定时任务,提前释放闲置资源。 - 修复缓存锁逻辑:所有业务用到的缓存锁必须设置最大过期时间,避免进程异常退出导致锁永久残留;可以额外配置每日定时任务,清理超过24小时的旧锁相关缓存键。
- 增加监控告警:配置Redis内存使用率、缓存键数量、慢查询数量的监控告警,达到阈值的80%就提前预警,避免累积到故障发生才处理。
内容的提问来源于stack exchange,提问作者Adam Lambert
相关产品推荐
相关产品推荐

