本文目录导读:

- 方案一:使用 Lazy Collection(推荐,Laravel 8+)
- 方案二:配合 Laravel Excel 的 Chunk Query(大数据专用)
- 方案三:手写分块查询 + 写入 CSV/Excel
- 性能与内存对比
- 额外优化技巧
对于 Laravel 导出大数据,强烈建议使用分块查询(Chunk),否则极易导致内存溢出或超时。
直接使用 Model::all() 或 DB::table('table')->get() 会将所有数据一次性加载到内存中,当数据量达到数十万或百万级时,PHP 进程会直接耗尽内存崩溃。
以下是针对 Laravel 大数据导出的最佳实践方案,按推荐程度排序:
使用 Lazy Collection(推荐,Laravel 8+)
这是目前最优雅、内存最友好的方式。cursor() 或 lazy() 使用生成器,一次只从数据库游标中读取一条记录。
<?php
namespace App\Exports;
use App\Models\User;
use Maatwebsite\Excel\Facades\Excel;
use Symfony\Component\HttpFoundation\BinaryFileResponse;
class UserExport
{
public function export(): BinaryFileResponse
{
return Excel::download(
new class implements \Maatwebsite\Excel\Concerns\FromCollection,
\Maatwebsite\Excel\Concerns\WithHeadings,
\Maatwebsite\Excel\Concerns\WithMapping
{
public function collection()
{
// 关键:使用 cursor() 或 lazy(),而不是 get()
return User::query()->cursor();
}
public function headings(): array
{
return ['ID', 'Name', 'Email'];
}
public function map($user): array
{
return [
$user->id,
$user->name,
$user->email,
];
}
},
'users.xlsx'
);
}
}
为什么有效?
cursor()使用 PHP 生成器(yield),内存占用几乎固定(与数据总量无关)。- 单个查询,不产生额外的磁盘 I/O。
配合 Laravel Excel 的 Chunk Query(大数据专用)
如果你用的是 Laravel Excel(maatwebsite/laravel-excel),官方提供了专门处理大数据的方法,会自动分块写入文件。
<?php
namespace App\Exports;
use App\Models\User;
use Maatwebsite\Excel\Concerns\FromQuery;
use Maatwebsite\Excel\Concerns\WithMapping;
use Maatwebsite\Excel\Concerns\WithHeadings;
use Maatwebsite\Excel\Concerns\ShouldAutoSize;
use Maatwebsite\Excel\Concerns\WithChunkReading;
class ChunkedUserExport implements FromQuery, WithMapping, WithHeadings, WithChunkReading
{
public function query()
{
// 返回一个 Query Builder,不用拷贝数据
return User::query();
}
public function headings(): array
{
return ['ID', 'Name', 'Email'];
}
public function map($user): array
{
return [
$user->id,
$user->name,
$user->email,
];
}
// 关键:指定每次从数据库读取多少条
public function chunkSize(): int
{
return 1000; // 建议 500-2000,根据内存大小和列数量调整
}
}
在 Controller 中调用:
Excel::store(new ChunkedUserExport(), 'users_big.xlsx');
手写分块查询 + 写入 CSV/Excel
如果不想用包,或者需要写入 CSV,可以手动实现分块:
use App\Models\User;
use Illuminate\Support\LazyCollection;
LazyCollection::make(function () {
User::query()->chunk(1000, function ($users) {
foreach ($users as $user) {
yield $user;
}
});
})
->map(function ($user) {
return [$user->id, $user->name, $user->email];
})
// 逐行写入 CSV,避免一次性内存堆积
->chunk(1000)
->each(function ($chunk) {
// 写入文件(追加模式)
$file = fopen(storage_path('app/export.csv'), 'a');
foreach ($chunk as $row) {
fputcsv($file, $row);
}
fclose($file);
});
性能与内存对比
| 方法 | 内存消耗 | 执行速度 | 适用数据量 | 建议 |
|---|---|---|---|---|
get() / all() |
❌ 极高 | 快 | < 1 万行 | 初学者错误 |
cursor() / lazy() |
✅ 极低 | 较快 | 百万级 | 首选 |
chunk() + CSV |
✅ 低 | 中等 | 百万级 | 无扩展时 |
Laravel Excel WithChunkReading |
✅ 极低 | 快 | 百万级+ | 推荐包方案 |
额外优化技巧
- 只选需要的列:
User::query()->select('id', 'name', 'email')->cursor();,避免 SELECT *。 - 增加 PHP 内存限制(不治本):
ini_set('memory_limit', '512M');,但分块方案不需要。 - 使用队列:对于超大数据(> 10 万行),建议使用 Laravel 队列异步导出,用户稍后下载。
Excel::queue(new ChunkedUserExport(), 'users.xlsx')->chain([ // 导出完成后通知用户 ]); - 避免 N+1:
cursor()配合with()使用时要小心,建议用lazy()或chunkById()。
- 不要用
get()或all()导出大数据。 - 推荐使用
cursor()(Laravel 8+ 默认支持)或lazy()。 - 若使用 Laravel Excel,务必实现
WithChunkReading。 - 数据量极大(> 50 万行)时,改用 CSV 格式(比 xlsx 快 10 倍以上)或使用队列。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播
英超直播