

新闻资讯
技术百科Lazy Collection 通过生成器按需获取数据,每次只取一批(默认1000行)并即时释放引用,避免全量加载;cursor()返回原始数组,lazy()创建完整模型实例。
Lazy Collection 不是把整个数据集读进内存,而是用生成器(Generator)按需产出每一项。它不会像普通 Collection 那样调用 get() 后立刻执行查询并实例化所有模型;相反,它把查询构建好,等到你真正遍历(比如用 each()、filter() 或 toArray())时才逐步 fetch —— 每次只取一批(默认 1000 行),且每行处理完就释放引用。
关键点在于:它底层包装的是 PDOStatement::fetch() 的迭代过程,不是 array_map 那种全量数组操作。
cursor() 而不是 lazy()
cursor() 和 lazy() 都返回 LazyCollection,但行为有本质区别:
cursor() 绕过 Eloquent 模型实例化,直接返回原始数组(stdClass 或关联数组),省掉模型构造、属性赋值、访问器调用等开销,内存更低、速度更快lazy() 仍会为每一行创建完整 Eloquent 模型,适合需要调用 $model->getAttribute()、$model->relation 或修改后保存的场景cursor();如果要复用模型逻辑(如 $user->fullName 访问器),再考虑 lazy()
示例对比:
App\Models\User::cursor()->each(function ($row) {
// $row 是 array 或 stdClass,无访问器、无事件、无类型转换
});
App\Models\User::lazy()->each(function ($user) {
// $user 是完整 User 模型实例,可调用 $user->name, $user->posts, $user->save()
});
LazyCol

count()、sum()、avg()、max()、min():必须遍历全部,但不会把所有模型留在内存 —— 只存中间结果(如累加值),这点比普通 Collection 好toArray()、all()、values():彻底放弃懒加载,把所有项转成数组,内存暴涨sortBy()、groupBy():需要随机访问或分组聚合,会先收集全部数据再处理,等价于 toArray() + 普通 Collection 操作filter()、map()、skip()、take()、chunk() —— 它们保持流式处理特性错误示范(看似懒,实则全载):
App\Models\LargeLog::lazy()
->sortBy('created_at') // ⚠️ 这里已把全部记录加载进内存
->take(10)
->each(...);
chunkById() 和游标分页进一步控内存LazyCollection 本身不解决单次查询太慢或 MySQL 连接超时问题。大数据集下,建议组合使用:
lazy(),改用 chunkById() 手动分页,每次查 WHERE id BETWEEN ? AND ?,避免 OFFSET 性能衰减cursorPaginate())代替 paginate(),避免 COUNT(*) 全表扫描streamDownload() + cursor() 直接写入响应流,完全不缓存结果例如流式 CSV 导出:
return response()->streamDownload(function () {
$handle = fopen('php://output', 'w');
fputcsv($handle, ['id', 'email', 'created_at']);
App\Models\User::cursor()->each(function ($user) use ($handle) {
fputcsv($handle, [$user->id, $user->email, $user->created_at]);
});
fclose($handle);
}, 'users.csv');
注意:cursor() 返回的字段名默认是数据库列名(如 created_at),不是模型访问器定义的键(如 createdAt),这点容易忽略。