麻烦阅读巨大的CSV文件与php fgetcsv -了解内存消耗


Trouble reading huge CSV file with php fgetcsv - understanding memory consumption

大家早上好!当我试图处理高达4GB的巨大csv文件时,我实际上正在经历一些艰难的教训。

目标是通过给定的browsenode和一些给定的项目id (ASIN)搜索csv文件(Amazon datafeed)中的一些项目。为了获得现有的物品(在我的数据库中)加上一些额外的新物品,因为不时有物品在市场上消失。我还过滤了项目的标题,因为有许多项目使用相同的标题。

我在这里读了很多技巧,最后决定使用php的fgetcsv(),并认为这个函数不会耗尽内存,因为它逐行读取文件。但是不管我怎么尝试,我总是内存不足。我不明白为什么我的代码使用这么多内存。

我设置内存限制为4096MB,时间限制为0。

服务器有64gb内存和两块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);
    }
}

我知道我的答案有点晚了,但我有一个类似的问题与fgets()和基于fgets()的东西,如SplFileObject->current()函数。在我的情况下,它是在windows系统上试图读取+800MB文件。我认为fgets()不会在循环中释放前一行的内存。因此,读取的每一行都留在内存中,并导致致命的内存不足错误。我使用fread($lineLength)来修复它,但它有点棘手,因为你必须提供长度。

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

你试过吗?SplFileObject:::

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

您正在耗尽内存,因为您使用变量,并且您从未执行unset();并使用太多嵌套的foreach。你可以在更多的函数中缩减代码一个解决方案应该是,使用一个真正的数据库。