Ubuntu下解决Laravel内存溢出的实用方案
一 快速定位与临时缓解
- 明确触发场景:是 Web 请求、Artisan 命令、还是 队列 Worker。
- 临时放宽内存上限(仅用于排查或一次性任务):
- 修改 PHP 配置(CLI 与 FPM 可能不同):/etc/php/7.x/cli/php.ini 或 /etc/php/7.x/fpm/php.ini,设置 memory_limit = 256M/512M;FPM 需重启服务。
- 在命令前临时生效:
- Web:在入口或中间件中
ini_set('memory_limit', '512M');
- Artisan:在命令类或调用处
ini_set('memory_limit', '1G');
- 注意:这只是权宜之计,需配合后续优化。
- 排查命令行的“假内存不足”:
- 关闭查询日志:
DB::connection()->disableQueryLog();
- 避免把整个结果集一次性装入内存:用 游标 cursor() 或 分块 chunk() 处理大数据。
二 代码与查询层面的根治
- 大数据导出/批处理:
- 使用 LazyCollection cursor() 逐行处理,避免
get() 把全部记录装入内存。
- 或用 chunk(10000…) 分页处理,边取边写,释放每批数据。
- 优化查询与关联:
- 解决 N+1 问题,使用 with(‘relation’) 预加载。
- 只选需要的字段,避免
select *。
- 减少日志与事件带来的内存压力:
- 长时任务或常驻进程可临时关闭查询日志:
DB::connection()->disableQueryLog();
- 排查第三方包对日志事件的监听(如 Facade/Ignition 的 LogRecorder 会累积日志到内存),必要时移除或在循环内手动
app()->make(LogRecorder::class)->reset(); 释放。
三 运行环境与进程管理
- 启用并优化 OPcache(减少重复编译带来的 CPU 与内存压力):
- zend_extension=opcache.so
- opcache.enable=1
- opcache.memory_consumption=128
- opcache.interned_strings_buffer=8
- opcache.max_accelerated_files=4000
- opcache.revalidate_freq=60
- 使用 队列 Worker 处理耗时任务,避免阻塞请求进程:
- 配置 Supervisor 守护:
sudo apt-get install supervisor
- 启动:
php artisan queue:work --queue=high,default --tries=3 --sleep=3
- 监控与维护:
- 监控内存:
htop、top、free -m
- 必要时定期重启服务(如异常增长):
sudo systemctl restart php**7.x**-fpm 或 sudo systemctl restart apache2。
四 常见场景与对应措施
| 场景 |
主要成因 |
推荐措施 |
| Web 请求偶发 OOM |
查询未优化、加载过多关联、日志/事件累积 |
优化查询与关联(with、字段筛选)、减少日志级别与事件监听、必要时临时上调 memory_limit |
| Artisan 大数据导出 |
一次性 get()、开启查询日志 |
使用 cursor() 或 chunk()、关闭查询日志、分批写入文件 |
| 队列 Worker 常驻内存上涨 |
日志/查询记录器在循环中累积 |
在循环内调用 LogRecorder::reset()、移除不必要的日志监听、控制日志级别与保留策略 |
| 配置已改仍 OOM |
CLI 与 FPM 配置不一致、OPcache 未启用 |
同步修改 CLI 与 FPM 的 php.ini、重启 FPM、启用并调优 OPcache |