【问题标题】:Process very big csv file without timeout and memory error处理非常大的 csv 文件,没有超时和内存错误
【发布时间】:2011-11-11 05:42:05
【问题描述】:

目前我正在为一个非常大的 CSV 文件编写导入脚本。问题大多是由于超时而在一段时间后停止或引发内存错误。

我的想法是现在以“100 行”步骤解析 CSV 文件,并在 100 行之后自动调用脚本。我试图用 header (location ...) 来实现这一点,并用 get 传递当前行,但它没有按我的意愿工作。

有没有更好的方法或者有人知道如何摆脱内存错误和超时?

【问题讨论】:

标签: php csv import timeout


【解决方案1】:

在内存消耗方面,fgetcsv() 和 fgets() 之间似乎存在巨大差异。 一个只有一列的简单 CSV 使用 fgetcsv() 通过了我的 512M 内存限制(仅 50000 条记录),并花了 8 分钟来报告。

使用 fgets() 只用了 3 分钟就成功处理了 649175 条记录,而且我的本地服务器甚至没有喘不过气来……

因此,如果 csv 中的列数有限,我的建议是使用 fgets()。在我的情况下, fgets() 直接返回第 1 列中的字符串。 对于不止一列,您可以在每次记录操作后 unset() 的一次性数组中使用explode()。 竖起大拇指的答案 3 @ndkauboy

【讨论】:

    【解决方案2】:

    我使用fgetcsv 以流式方式读取 120MB 的 csv(英文正确吗?)。它逐行读取,然后我将每一行都插入到数据库中。这样,每次迭代时只有一行保存在内存中。脚本仍然需要 20 分钟。跑步。也许我下次试试 Python……不要尝试将巨大的 csv 文件加载到数组中,那样真的会消耗大量内存。

    // WDI_GDF_Data.csv (120.4MB) are the World Bank collection of development indicators:
    // http://data.worldbank.org/data-catalog/world-development-indicators
    if(($handle = fopen('WDI_GDF_Data.csv', 'r')) !== false)
    {
        // get the first row, which contains the column-titles (if necessary)
        $header = fgetcsv($handle);
    
        // loop through the file line-by-line
        while(($data = fgetcsv($handle)) !== false)
        {
            // resort/rewrite data and insert into DB here
            // try to use conditions sparingly here, as those will cause slow-performance
    
            // I don't know if this is really necessary, but it couldn't harm;
            // see also: http://php.net/manual/en/features.gc.php
            unset($data);
        }
        fclose($handle);
    }
    

    【讨论】:

      【解决方案3】:

      我发现使用 mysql 的 LOAD DATA LOCAL 查询上传文件并插入是一个快速的解决方案,例如:

          $sql = "LOAD DATA LOCAL INFILE '/path/to/file.csv' 
              REPLACE INTO TABLE table_name FIELDS TERMINATED BY ',' 
              ENCLOSED BY '\"' LINES TERMINATED BY '\r\n' IGNORE 1 LINES";
          $result = $mysqli->query($sql);
      

      【讨论】:

      • 哇,我从 5 分钟+ 导入 64000 条记录 csv 到不到 5 秒。这太棒了!
      【解决方案4】:

      哦。只需将此脚本称为 CLI,而不是通过愚蠢的 Web 界面。所以,没有执行时间限制会影响它。
      并且不要永远保留解析结果,而是立即将它们写下来 - 这样,您也不会受到内存限制的影响。

      【讨论】:

        【解决方案5】:

        如果您不关心它需要多长时间和需要多少内存,您可以简单地增加此脚本的值。只需将以下行添加到脚本的顶部:

        ini_set('memory_limit', '512M');
        ini_set('max_execution_time', '180');
        

        使用函数memory_get_usage(),您可以找出脚本需要多少内存才能找到合适的memory_limit值。

        您可能还想查看fgets(),它允许您逐行读取文件。我不确定这是否需要更少的内存,但我真的认为这会奏效。但即使在这种情况下,您也必须将 max_execution_time 增加到更高的值。

        【讨论】:

        • 如果您知道文件始终具有相同的大小,这当然是一个好方法。
        • 如果您知道它不大于特定尺寸,它也可以。
        猜你喜欢
        • 1970-01-01
        • 2013-01-11
        • 1970-01-01
        • 1970-01-01
        • 2017-07-15
        • 2023-03-25
        • 1970-01-01
        • 2014-04-14
        • 2013-05-07
        相关资源
        最近更新 更多