Laravel使用Maatwebsite导入5亿行大CSV超时问题求助
大CSV高效导入解决方案(5亿行)
针对Laravel中Maatwebsite导入超大型CSV超时的问题,以下是无需延长超时时间的优化方案:
1. 启用Maatwebsite分块导入
默认逐行处理会频繁触发数据库操作,分块批量处理能大幅减少交互次数:
修改你的导入类,实现WithChunkReading接口,设置合理的分块大小(根据服务器内存调整,建议5000-10000):
use Maatwebsite\Excel\Concerns\ToModel; use Maatwebsite\Excel\Concerns\WithChunkReading; class DutchPostalcodes implements ToModel, WithChunkReading { public function model(array $row) { return new DutchPostalcode([ 'postalcode' => $row['PostcodeID'], 'street' => $row['Straat'], 'place' => $row['Plaats'], 'government' => $row['Gemeente'], 'province' => $row['Provincie'], 'latitude' => $row['Latitude'], 'longtitude' => $row['Longitude'], ]); } // 设置分块大小 public function chunkSize(): int { return 5000; } }
2. 替换模型创建为批量插入(跳过模型事件)
模型的save()方法会触发各类事件(creating、saved等),批量插入直接操作数据库能显著提速。改用ToCollection接口处理:
use Maatwebsite\Excel\Concerns\ToCollection; use Maatwebsite\Excel\Concerns\WithChunkReading; use Illuminate\Support\Collection; class DutchPostalcodes implements ToCollection, WithChunkReading { public function collection(Collection $rows) { // 转换数据格式 $batchData = $rows->map(function ($row) { return [ 'postalcode' => $row['PostcodeID'], 'street' => $row['Straat'], 'place' => $row['Plaats'], 'government' => $row['Gemeente'], 'province' => $row['Provincie'], 'latitude' => $row['Latitude'], 'longtitude' => $row['Longitude'], ]; })->toArray(); // 禁用查询日志,避免内存占用 \DB::disableQueryLog(); // 事务内批量插入 \DB::transaction(function () use ($batchData) { \App\Models\DutchPostalcode::insert($batchData); }); } public function chunkSize(): int { return 10000; } }
3. 使用数据库原生导入命令(最快方案)
直接调用数据库的LOAD DATA INFILE命令,这是效率最高的方式,和Datagrip的导入逻辑一致:
public function writeDutchPostalCodes() { $filePath = 'C:\Users\Moeme\Documents\Projects\ahmo apps\Apps\freshness\Freshness - be\FreshnessBE\resources\postalcodes\postcodetabel_1.csv'; // 禁用查询日志 \DB::disableQueryLog(); // 执行原生LOAD DATA命令 \DB::unprepared(" LOAD DATA INFILE '" . addslashes($filePath) . "' INTO TABLE dutch_postalcodes FIELDS TERMINATED BY ',' ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 ROWS (postalcode, street, place, government, province, latitude, longtitude) "); }
注意:需要确保MySQL的
secure_file_priv配置允许读取该文件路径,若限制路径,可将CSV文件移动到MySQL允许的目录后再执行。
额外优化建议
- 临时关闭表的索引和外键约束:导入前执行
ALTER TABLE dutch_postalcodes DISABLE KEYS;,导入完成后执行ALTER TABLE dutch_postalcodes ENABLE KEYS;,减少索引维护开销。 - 调整服务器配置:提升PHP的
memory_limit避免内存溢出;优化数据库配置(如增大innodb_buffer_pool_size)。
内容的提问来源于stack exchange,提问作者Moemen Hussein
相关产品推荐
相关产品推荐

