ปัญหาในการอ่านไฟล์ CSV ขนาดใหญ่ด้วย php fgetcsv - ทำความเข้าใจการใช้หน่วยความจำ

สวัสดีตอนเช้า ฉันกำลังเรียนบทเรียนหนักๆ ขณะพยายามจัดการไฟล์ CSV ขนาดใหญ่สูงสุด 4GB

เป้าหมายคือการค้นหาบางรายการในไฟล์ csv (ฟีดข้อมูลของ Amazon) โดย browsernode ที่ระบุและโดยบางรายการ id's (ASIN) เพื่อให้ได้การผสมผสานระหว่างรายการที่มีอยู่ (ในฐานข้อมูลของฉัน) บวกกับรายการใหม่เพิ่มเติม เนื่องจากรายการหายไปในตลาดเป็นครั้งคราว ฉันยังกรองชื่อของรายการเนื่องจากมีหลายรายการที่ใช้เหมือนกัน

ฉันได้อ่านเคล็ดลับต่างๆ มากมายที่นี่ และในที่สุดก็ตัดสินใจใช้ fgetcsv() ของ php และคิดว่าฟังก์ชันนี้จะไม่ทำให้หน่วยความจำหมด เนื่องจากมันจะอ่านไฟล์ทีละบรรทัด แต่ไม่ว่าฉันจะพยายามทำอะไร ความทรงจำของฉันก็จะหมดอยู่เสมอ ฉันไม่เข้าใจว่าทำไมรหัสของฉันจึงใช้หน่วยความจำมาก

ฉันตั้งค่าขีดจำกัดหน่วยความจำเป็น 4096MB ขีดจำกัดเวลาคือ 0 เซิร์ฟเวอร์มี RAM 64 GB และฮาร์ดดิส 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);
    }
}

person Thomas Tonius    schedule 17.09.2015    source แหล่งที่มา


คำตอบ (3)


ฉันรู้ว่าคำตอบของฉันช้าไปหน่อย แต่ฉันมีปัญหาคล้ายกันกับ fgets() และสิ่งต่าง ๆ ตาม fgets() เช่น SplFileObject->current() function ในกรณีของฉัน มันเป็นระบบ Windows เมื่อพยายามอ่านไฟล์ +800MB ฉันคิดว่า fgets() ไม่ทำให้หน่วยความจำของบรรทัดก่อนหน้า วนซ้ำ ดังนั้นทุกบรรทัดที่อ่านจะยังคงอยู่ในหน่วยความจำและปล่อยให้ ข้อผิดพลาดหน่วยความจำไม่เพียงพอร้ายแรง ฉันแก้ไขโดยใช้ fread($lineLength) แทน แต่มันยุ่งยากกว่าเล็กน้อยเนื่องจากคุณต้องระบุความยาว

person lorenzobe    schedule 20.05.2016

เป็นเรื่องยากมากในการจัดการข้อมูลขนาดใหญ่โดยใช้อาเรย์โดยไม่ต้องประสบปัญหาการหมดเวลา ทำไมไม่แยกวิเคราะห์ฟีดข้อมูลนี้ไปยังตารางฐานข้อมูลแล้วทำการยกของหนักจากที่นั่น

person MACMAN    schedule 17.09.2015
comment
ดี. ฉันแค่คิดว่าประสิทธิภาพโดยรวมจะดีกว่าหากไม่ใช้ฐานข้อมูลและแทนที่จะใช้ไฟล์ csv ธรรมดา เนื่องจาก Amazon จำเป็นต้องอัปเดตข้อมูลเป็นประจำ (อย่างน้อยทุกๆ 24 ชั่วโมง) ซึ่งหมายถึงการเปรียบเทียบฟีดพื้นฐานกับฟีดอัปเดต ซึ่งสามารถเกิดขึ้นได้ทุก 30 นาที - person Thomas Tonius; 17.09.2015
comment
หากเป็นไปได้ให้ลองแยกไฟล์ขนาดใหญ่ออกเป็นหลายๆ ไฟล์ ค้นหาเครื่องมือที่สามารถทำให้สิ่งนั้นสำเร็จได้ - person MACMAN; 17.09.2015
comment
อืม ฉันยังคงหวังว่าถ้ามีคนอธิบายวิธีใช้หน่วยความจำโดยละเอียดได้ ก็สามารถจัดการได้ภายในไฟล์เดียว ฉันมีไฟล์ที่ต้องจัดการอยู่แล้ว 82 ไฟล์ - person Thomas Tonius; 17.09.2015
comment
บางทีกระทู้นี้อาจช่วยคุณได้ stackoverflow.com/questions/5249279/ - person MACMAN; 17.09.2015

คุณได้ลองสิ่งนี้แล้วหรือยัง? SplFileObject::fgetcsv

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

หน่วยความจำของคุณกำลังจะไม่เพียงพอเนื่องจากคุณใช้ตัวแปร และคุณไม่เคยทำ unset(); และใช้ foreach ที่ซ้อนกันมากเกินไป คุณสามารถย่อขนาดโค้ดนั้นในฟังก์ชันเพิ่มเติมได้ วิธีแก้ปัญหาควรใช้ฐานข้อมูลจริงแทน

person Elias Nicolas    schedule 17.09.2015
comment
ฉันไม่ต้องการใช้ฐานข้อมูล เนื่องจากฉันต้องอัปเดตฟีดเหล่านี้เป็นประจำ ฉันใช้อาร์เรย์สามตัวที่รวบรวมข้อมูลและสามารถมีค่าสูงสุด 200 ค่า (แต่ละค่า) อาร์เรย์ที่สี่คือ $header ซึ่งถูกรีเซ็ตสำหรับแต่ละบรรทัด และฉันไม่ได้รีเซ็ตตัวแปรเนื่องจากจะถูกรีเซ็ตหลังจากทุกบรรทัดอ่านใน while วนซ้ำ แต่ขอบคุณสำหรับการโพสต์ SplFileObject - ฉันจะอ่านคู่มือ - person Thomas Tonius; 17.09.2015
comment
ฉันได้ลองแล้ว แต่ก็ไม่ได้ช่วยอะไร - แต่ยังไงก็ขอบคุณ! - person Thomas Tonius; 17.09.2015