Laravel 5.5(Homestead)修改php.ini memory_limit后仍内存耗尽报错
老哥,你这个问题我之前帮朋友排查过好几次——核心问题不是memory_limit调得不够,而是你直接用get()把整张表的数据一次性塞进内存了,哪怕你把内存调到天上去,只要数据量够大,迟早还是会爆。而且Homestead里PHP的CLI和Web配置是分开的,你大概率改的是Web用的php.ini,而数据导入脚本是通过CLI运行的,所以改了也白搭。
给你几个实打实的解决方案,按优先级来:
1. 用分块查询(Chunk)替代一次性获取
Laravel的查询构造器自带chunk()方法,它会每次只加载一小部分数据到内存,处理完就释放,完美解决内存溢出问题。比如把每次处理的数量设为100(你可以根据自己的服务器内存调整,500、1000都试试):
DB::connection('db')->table('tablename')->chunk(100, function ($repairsBatch) { DB::beginTransaction(); try { foreach ($repairsBatch as $repair) { Model::create([ 'col1' => $repair->col1, 'col2' => $repair->col2, // 你的其他字段映射 ]); } DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw $e; // 或者根据需求处理异常,比如记录日志 } });
2. 检查并修改CLI版本的php.ini
Homestead里,Web服务器用的PHP配置和命令行(CLI)用的是两个不同的文件。你可以通过SSH登录Homestead后,运行php --ini命令,找到Loaded Configuration File对应的路径,那才是CLI模式用的php.ini文件。修改这个文件里的memory_limit(比如设为512M或者1G),然后新开一个终端窗口就生效了(CLI配置不需要重启服务)。
3. 用游标(Cursor)实现更低内存占用
如果你的数据量超大,几百万甚至上千万条,可以用cursor()方法,它返回一个Generator对象,每次只从数据库取一条数据,内存占用几乎可以忽略:
$repairCursor = DB::connection('db')->table('tablename')->cursor(); DB::beginTransaction(); try { foreach ($repairCursor as $repair) { Model::create([ // 字段映射 ]); } DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw $e; }
不过注意:如果把事务包在整个循环外面,一旦中间出错会回滚所有已插入的数据。如果数据量特别大,建议还是用分块+每块单独事务,避免一次回滚太多数据。
4. 批量插入优化性能和内存
如果你的数据不需要触发Model的created等事件,直接用批量插入insert()方法,比循环调用create()效率高N倍,内存占用也更低:
DB::connection('db')->table('tablename')->chunk(500, function ($repairsBatch) { $insertData = []; foreach ($repairsBatch as $repair) { $insertData[] = [ 'col1' => $repair->col1, 'col2' => $repair->col2, // 其他字段 ]; } DB::beginTransaction(); try { Model::insert($insertData); DB::commit(); } catch (\Exception $e) { DB::rollBack(); throw $e; } });
这个方法把每批数据打包成数组一次性插入数据库,减少了数据库连接次数,性能提升非常明显。
内容的提问来源于stack exchange,提问作者Hashaam Ahmed

