无法使用php fgetcsv读取巨大的CSV文件 - 了解内存消耗

时间:2015-09-17 04:19:14

标签: php out-of-memory fgetcsv

早上好, 我在尝试处理高达4GB的巨大csv文件时,实际上正在经历一些艰苦的课程。

目标是通过给定的browsenode以及某些给定的项目ID(ASIN)搜索csv文件(Amazon datafeed)中的某些项目。为了获得现有项目的混合(在我的数据库中)以及一些额外的新主题,因为市场上的项目不时消失。我也过滤了项目的标题,因为有许多项目使用相同的。

我一直在这里阅读很多提示,最后决定使用php的fgetcsv()并认为这个函数不会耗尽内存,因为它逐行读取文件。 但无论我尝试什么,我总是会耗尽内存。 我无法理解为什么我的代码使用了这么多内存。

我将内存限制设置为4096MB,时间限制为0.服务器有64 GB Ram和两个SSD硬盘。

请有人请查看我的代码并解释内存耗尽的可能性以及更重要的内存使用情况?

private function performSearchByASINs()
{
    $found = 0;
    $needed = 0;
    $minimum = 84;
    if(is_array($this->searchASINs) && !empty($this->searchASINs))
    {
        $needed = count($this->searchASINs);
    }
    if($this->searchFeed == NULL || $this->searchFeed == '')
    {
        return false;
    }
    $csv = fopen($this->searchFeed, 'r');
    if($csv)
    {
        $l = 0;
        $title_array = array();
        while(($line = fgetcsv($csv, 0, ',', '"')) !== false)
        {
            $header = array();
            if(trim($line[6]) != '')
            {
                if($l == 0)
                {
                    $header = $line;
                }
                else
                {
                    $asin = $line[0];
                    $title = $this->prepTitleDesc($line[6]);
                    if(is_array($this->searchASINs) 
                    && !empty($this->searchASINs) 
                    && in_array($asin, $this->searchASINs)) //search for existing items to get them updated
                    {
                        $add = true;
                        if(in_array($title, $title_array))
                        {
                            $add = false; 
                        }
                        if($add === true)
                        {
                            $this->itemsByASIN[$asin] = new stdClass();
                            foreach($header as $k => $key)
                            {
                                if(isset($line[$k]))
                                {
                                    $this->itemsByASIN[$asin]->$key = trim(strip_tags($line[$k], '<br><br/><ul><li>'));
                                }
                            }
                            $title_array[] = $title;
                            $found++;
                        }
                    }
                    if(($line[20] == $this->bnid || $line[21] == $this->bnid) 
                    && count($this->itemsByKey) < $minimum 
                    && !isset($this->itemsByASIN[$asin])) // searching for new items
                    {
                        $add = true;
                        if(in_array($title, $title_array))
                        {
                           $add = false;
                        }
                        if($add === true)
                        {
                            $this->itemsByKey[$asin] = new stdClass();
                            foreach($header as $k => $key)
                            {
                                if(isset($line[$k]))
                                {
                                    $this->itemsByKey[$asin]->$key = trim(strip_tags($line[$k], '<br><br/><ul><li>'));                                
                                }
                            }
                            $title_array[] = $title;
                            $found++;
                        }
                    }
                }
                $l++;
                if($l > 200000 || $found == $minimum)
                {
                    break;
                }
            }
        }
        fclose($csv);
    }
}

3 个答案:

答案 0 :(得分:2)

我知道我的答案有点迟了但我遇到了与fgets()类似的问题以及基于fgets()的问题,例如SplFileObject->current()函数。在我的情况下,当尝试读取 + 800MB 文件时,它在Windows系统上。我认为fgets()不会释放上一行的循环的内存。所以读取的每一行都留在内存中,让致命的内存不足错误。我使用fread($lineLength)修复了它,但它有点棘手,因为你必须提供长度。

答案 1 :(得分:0)

使用数组管理大数据非常困难而不会遇到超时问题。相反,为什么不将这个数据源解析到数据库表并从那里进行繁重的工作。

答案 2 :(得分:0)

你试过这个吗? SplFileObject::fgetcsv

<?php
$file = new SplFileObject("data.csv");
while (!$file->eof()) {
    //your code here
}
?>

由于您使用变量而导致内存不足,而您永远不会使用unset();并且使用过多的嵌套foreach。您可以在更多函数中缩小该代码 解决方案应该是,使用真正的数据库。