Kesulitan membaca file CSV besar dengan php fgetcsv - memahami konsumsi memori

Selamat pagi, Saya sebenarnya sedang menjalani beberapa pelajaran sulit saat mencoba menangani file csv berukuran besar hingga 4 GB.

Tujuannya adalah untuk mencari beberapa item dalam file csv (datafeed Amazon) dengan node browser tertentu dan juga dengan beberapa id item tertentu (ASIN). Untuk mendapatkan campuran item yang ada (di database saya) ditambah beberapa item baru tambahan karena dari waktu ke waktu item menghilang di pasar. Saya juga memfilter judul item karena banyak item yang menggunakan judul yang sama.

Saya telah membaca banyak tips di sini dan akhirnya memutuskan untuk menggunakan fgetcsv() php dan berpikir fungsi ini tidak akan menghabiskan memori, karena membaca file baris demi baris. Tapi apa pun yang saya coba, saya selalu kehabisan memori. Saya tidak mengerti mengapa kode saya menggunakan begitu banyak memori.

Saya menetapkan batas memori ke 4096MB, batas waktu adalah 0. Server memiliki Ram 64 GB dan dua harddisk SSD.

Bolehkah seseorang memeriksa potongan kode saya dan menjelaskan bagaimana mungkin saya kehabisan memori dan yang lebih penting bagaimana memori digunakan?

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);
    }
}

person Thomas Tonius    schedule 17.09.2015    source sumber


Jawaban (3)


Saya tahu jawaban saya agak terlambat tetapi saya memiliki masalah serupa dengan fgets() dan hal-hal berdasarkan fungsi fgets() seperti SplFileObject->current(). Dalam kasus saya, ini terjadi pada sistem windows ketika mencoba membaca file +800MB. Saya pikir fgets() tidak mengosongkan memori baris sebelumnya dalam satu lingkaran. Jadi setiap baris yang dibaca tetap tersimpan di memori dan menyebabkan kesalahan memori yang fatal. Saya memperbaikinya menggunakan fread($lineLength) tetapi ini sedikit lebih rumit karena Anda harus menyediakan panjangnya.

person lorenzobe    schedule 20.05.2016

Sangat sulit untuk mengelola data besar menggunakan array tanpa menemui masalah batas waktu. Mengapa tidak mengurai datafeed ini ke tabel database dan melakukan pekerjaan berat dari sana.

person MACMAN    schedule 17.09.2015
comment
Dengan baik. Saya hanya berpikir kinerja secara keseluruhan akan lebih baik tidak menggunakan database dan daripada menggunakan file csv biasa, karena Amazon perlu memperbarui data secara teratur (setidaknya setiap 24 jam), yang berarti membandingkan basefeed dengan updatefeeds yang bisa muncul setiap 30 menit - person Thomas Tonius; 17.09.2015
comment
Jika memungkinkan coba pisahkan file besar menjadi beberapa file. Cari alat yang dapat mencapai hal itu - person MACMAN; 17.09.2015
comment
Hmm, saya masih berharap jika ada yang bisa menjelaskan secara detail penggunaan memori bisa ditangani dalam satu file. Saya sudah memiliki 82 file untuk ditangani. - person Thomas Tonius; 17.09.2015
comment
Mungkin thread ini bisa membantu Anda. stackoverflow.com/questions/5249279/ - person MACMAN; 17.09.2015

Sudahkah Anda mencoba ini? SplFileObject::fgetcsv

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

Anda kehabisan memori karena menggunakan variabel, dan Anda tidak pernah melakukan unset(); dan menggunakan terlalu banyak foreach bersarang. Anda dapat mengecilkan kode itu di lebih banyak fungsi. Solusinya adalah, gunakan Database yang sebenarnya.

person Elias Nicolas    schedule 17.09.2015
comment
Saya tidak ingin menggunakan database, karena saya harus memperbarui feed ini secara teratur. Saya menggunakan tiga array yang mengumpulkan data dan dapat berisi maksimal 200 nilai (masing-masing), Array keempat adalah $header yang disetel ulang untuk setiap baris dan saya tidak menyetel ulang variabel karena disetel ulang setelah setiap baris dibaca di loop while. Namun terima kasih telah memposting SplFileObject - Saya akan membaca manualnya - person Thomas Tonius; 17.09.2015
comment
Saya sudah mencoba ini, tetapi tidak membantu - tapi terima kasih! - person Thomas Tonius; 17.09.2015