Laravel 8 大数据查询应避免 all() 防内存溢出,改用 cursor()(轻量、无模型能力)或 lazy()(完整模型功能);禁用 count()、toArray()、sortBy() 等会触发全量加载的操作。

当你的 Laravel 8 应用需要从数据库读取数万甚至百万级用户、订单或日志记录时,直接调用 all() 会把全部结果一次性加载进内存,极易触发 PHP 内存溢出(如 Fatal error: Allowed memory size of ... exhausted),导致请求崩溃或服务不稳定。
为什么 all() 在大数据场景下危险
调用 User::all() 等价于执行 SELECT * FROM users → 触发 get() → PDO 返回全部结果集 → Laravel 将每行转为完整 Eloquent 模型实例 → 所有模型对象常驻内存直至脚本结束。哪怕你只遍历一次并立即丢弃,PHP 垃圾回收也无法及时释放——因为整个集合数组仍被变量引用。
【关键风险】 即使你后续只用 foreach 逐个处理,all() 已提前耗尽全部内存,无法回退。
LazyCollection 如何真正“懒”起来
LazyCollection 不是靠“延迟 foreach”来省内存,而是彻底绕过全量加载机制:它用 PHP 生成器(yield)封装查询执行过程,每次仅 fetch 一批(默认 1000 行),处理完立刻释放这批模型的引用,下一批才重新拉取。
底层不依赖 get(),而是直接操作 PDOStatement::fetch() 迭代器,跳过中间数组拼装环节,内存占用呈线性而非爆炸式增长。
两种推荐替代方案及适用选择
方法一:用 cursor() —— 最轻量,适合导出、统计、字段清洗
① 直接在查询末尾加 ->cursor():User::where('status', 'active')->orderBy('id')->cursor()
② 遍历时拿到的是 stdClass 或关联数组,无模型访问器、无事件、无类型转换,内存最低、速度最快
③ 示例:User::cursor()->each(fn($row) => file_put_contents('export.csv', "$row->name,$row->email\n", FILE_APPEND));
注意:此时 $row->posts 会报错——它根本不是模型,不支持关系访问。
方法二:用 lazy() —— 保留完整模型能力,适合需调用访问器或保存的场景
直接替换:User::where('status', 'active')->lazy()
返回的每个 $user 是真正的 User 实例,可安全调用 $user->fullName、$user->posts、$user->save()
【易错提醒】 如果你只写 lazy()->each() 却没调用任何模型方法,纯属浪费性能——此时应换回 cursor()。
必须避开的“伪懒”陷阱
以下操作会让 LazyCollection 瞬间失去惰性,退化成 all() 级别的内存压力:
count()、sum()、avg():虽只存中间值,但必须遍历全部数据
toArray()、all()、values():强制把所有项转为数组,内存暴涨,【绝对禁止】 在大数据流中调用
sortBy()、groupBy():需随机访问或聚合分组,内部会先 toArray() 再处理,等价于放弃懒加载
load() 和 loadMissing():这两个方法根本不接受 LazyCollection,调用即报错 Call to undefined method Illuminate\Support\LazyCollection::load()


















