Laravel Horizon场景下Redis内存占满2GB问题排查
问题排查与解决方案
一、maxmemory=0但存在2GB内存上限的原因与修复
maxmemory=0仅代表Redis自身不设置内存使用阈值,实际可使用的内存上限受三层外部约束,按出现概率从高到低排查:
- 32位Redis实例硬限制:32位程序的用户态内存寻址空间上限为2GB,无论怎么调整Redis配置都无法突破该值。执行
redis-cli info server | grep arch_bits即可验证,返回结果为arch_bits:32时直接重装64位版本Redis即可解决。 - 部署环境内存限制:如果是容器化部署,检查容器是否配置了2GB内存配额;物理机/虚拟机部署的话,检查Redis运行用户是否被配置了cgroup内存限制、ulimit虚拟内存上限,这类限制会在进程申请内存超过阈值时直接触发内存分配失败。
- 注意:你之前遇到的
Allowed memory size of 536870912 bytes exhausted是PHP进程的内存报错,和Redis内存限制无关,这个报错代表Horizon的PHP工作进程内存占用超过了php.ini中memory_limit配置的512M阈值,属于独立问题。
提示:当前实例内存峰值已经到2.04G,如果是32位Redis场景,已经非常接近硬上限,随时可能触发OOM被系统终止,优先排查该问题。
二、Horizon内存占用异常(裁剪失效)排查方向
你当前的trim参数配置本身没有语法问题,低任务量下占满2G内存基本都是裁剪逻辑未正常执行,或是存在未被纳入默认裁剪范围的冗余key,按以下顺序排查:
- 先定位大key来源
执行redis-cli --bigkeys -i 0.1扫描全实例大key,重点匹配horizon:*前缀的key:- 如果
horizon:recent:*、horizon:completed:*、horizon:failed:*这类列表、有序集合key体积异常偏大,可直接判定为裁剪逻辑失效 - 如果大key属于其他业务前缀,说明该Redis实例并非仅承载Horizon负载,内存占用来自其他业务缓存
- 如果
- 验证Horizon裁剪任务的触发链路
Horizon的历史任务裁剪不是实时执行的,依赖内置定时任务触发,整条链路任意一环断裂都会导致裁剪失效:- 先执行
php artisan horizon:status确认Horizon主进程状态为active,如果进程频繁崩溃重启,定时任务自然无法正常调度 - 检查
app/Console/Kernel.php中是否注册了Horizon的定时任务,必须包含$schedule->command('horizon:snapshot')->everyFiveMinutes();配置,否则裁剪、指标统计等内置定时逻辑都不会执行 - 检查服务器crontab是否配置了Laravel调度入口,必须存在
* * * * * cd /你的项目绝对路径 && php artisan schedule:run >> /dev/null 2>&1条目,否则所有Laravel框架的定时任务都不会触发
- 先执行
- 排查漏裁剪的特殊场景
- 如果业务中使用了
Horizon::tag()给任务打标签,标签关联的任务记录不会被默认trim逻辑覆盖,需要在配置中新增trim.tags参数指定留存时长(单位分钟),未配置时标签关联记录会永久留存 - 如果配置了
Horizon::monitor()监控指定任务,你当前monitored参数设置的留存时长为10080分钟(7天),如果被监控的任务是高频任务,会堆积大量历史记录,可以根据业务需求适当调小该值 - 长期运行的Horizon实例会留存大量分钟、小时级的指标统计数据,这些
horizon:metrics:*前缀的key如果长期不清理也会占用可观内存,可以先执行php artisan horizon:purge清理冗余历史指标
- 如果业务中使用了
- 快速验证裁剪逻辑
手动执行php artisan horizon:trim,执行完成后观察Redis内存占用,如果出现明显下降,可100%确定是定时调度链路配置缺失导致的裁剪失效,补全上述调度配置即可。
三、临时应急处理
如果当前内存占用已经接近阈值存在业务风险,可先执行php artisan horizon:trim --force强制裁剪所有历史任务记录,再执行redis-cli memory purge主动回收Redis内存碎片,可快速将内存占用降到合理水平,后续再按上述步骤排查根因。
内容的提问来源于stack exchange,提问作者M. Ravdel
相关产品推荐
相关产品推荐

