Laravel v8.52.0使用Eloquent Model save()做数据库迁移时内存溢出排查
Laravel 8.52.0 Eloquent批量操作内存溢出问题解决方案
问题场景
在Laravel v8.52.0版本下使用Eloquent Model执行批量数据库迁移操作时,出现内存持续上涨最终溢出的错误,测试代码如下:
public function handle() { for ($i = 0; $i < 100; $i++) { Customer::chunkById(1000, function ($customers) use ($i) { $this->print_progress(); foreach ($customers as $customer) { $customer->first_name = (string)$i; $customer->save(); } }); } }
错误日志
运行时内存线性攀升,最终触发PHP内存上限报错,日志内容如下:
usage: 27MB - peek: 27MB usage: 33MB - peek: 33MB usage: 39MB - peek: 39MB ... ... usage: 491MB - peek: 491MB usage: 496MB - peek: 496MB PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 20480 bytes) in /home/vagrant/code/billing/vendor/laravel/framework/src/Illuminate/Support/Str.php on line 855 PHP Fatal error: Allowed memory size of 536870912 bytes exhausted (tried to allocate 32768 bytes) in /home/vagrant/code/billing/vendor/symfony/error-handler/Error/FatalError.php on line 1
根因定位
内存泄漏由Laravel官方的Telescope调试组件导致,该组件默认会记录所有数据库查询、模型操作等运行日志,且在命令行执行长耗时批量任务时不会自动释放已记录的日志内存,随着操作量增加内存占用持续上涨,最终触发溢出。
修复方案
- 全局临时关闭Telescope:执行批量任务前在
.env配置文件中修改参数TELESCOPE_ENABLED=false,任务执行完成后再按需开启即可完全解决该场景下的内存泄漏问题。 - 单任务禁用Telescope记录:如果不需要全局关闭Telescope,可在任务代码开头添加如下代码,仅对当前批量任务禁用日志记录:
\Laravel\Telescope\Telescope::stopRecording();
- 可选性能优化:现有逐行调用
save()的逻辑数据库IO开销较高,可调整为批量更新逻辑进一步降低任务耗时和资源占用,示例如下:
public function handle() { for ($i = 0; $i < 100; $i++) { Customer::where('id', '>', $i * 10000) ->where('id', '<=', ($i + 1) * 10000) ->update(['first_name' => (string)$i]); $this->print_progress(); } }
内容的提问来源于stack exchange,提问作者Shengtian Liao
相关产品推荐
相关产品推荐

