You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.12 15:05:30