MySQL: проблема выделения памяти

Я использую WAMP на своем компьютере, и когда я запускаю алгоритм, который импортирует данные из файла Excel, это занимает слишком много времени. Во-первых, выдает ошибку, что недостаточно памяти. Затем я продлил memory_limitпараметр на php.ini, Но опять же, на этот раз, он работает долгое время, а затем просто показывает знаменитую "Страница не может быть отображена" (соединение прервано).

Кроме того, этот алгоритм должен импортировать только 5 строк. Но это немного долго. Он создает объекты на основе значений, которые он получает из файла Excel, и Stuf..

Так что ты думаешь?

NB: я использую Symfony2, Doctrine2 и библиотеку PHPExcel, которая помогает манипулировать файлами, подобными Excel.

Изменить (вот код):

<?php

public function bachelierAction(){

        $inputFileType = 'Excel2007';
        $inputFileName = 'Excel_Files/Bacheliers/Bacheliers12.xlsx';


        /**  Create a new Reader of the type defined in $inputFileType  **/
        $objReader = \PHPExcel_IOFactory::createReader($inputFileType);
        $objReader->setReadDataOnly(true);
        /**  Define how many rows we want to read for each "chunk"  **/
        $chunkSize = 1;
        /**  Create a new Instance of our Read Filter  **/
        $chunkFilter = new chunkReadFilter();

        /**  Tell the Reader that we want to use the Read Filter  **/
        $objReader->setReadFilter($chunkFilter);
        $objPHPExcel = $objReader->load($inputFileName);

        // The Entity Manager :
        $em = $this->getDoctrine()->getManager();

        $anneObj = new Annee();
        $anneObj->setAnnee(2012);


        for ($startRow = 2; $startRow <= 6 ; $startRow += $chunkSize) {
            /*  Tell the Read Filter which rows we want this iteration  */
            $chunkFilter->setRows($startRow,$chunkSize);
            /**  Load only the rows that match our filter  **/
            $objPHPExcel = $objReader->load($inputFileName);
            $sheet = $objPHPExcel->getSheet(0);

            // Extraire les données du bachelier :
            $cne = $sheet->getCellByColumnAndRow(1,$startRow)->getValue();
            $nom = $sheet->getCellByColumnAndRow(2,$startRow)->getValue();
            $prenom = $sheet->getCellByColumnAndRow(3,$startRow)->getValue();
            $sexe = $sheet->getCellByColumnAndRow(5,$startRow)->getValue();;
            $dateNaissance = $sheet->getCellByColumnAndRow(6,$startRow)->getValue();

            // Création du bachelier
            $bachelier = new Bachelier();
            $bachelier->setCne($cne)
                      ->setDateNaissance($dateNaissance)
                      ->setNom($nom)
                      ->setPrenom($prenom)
                      ->setSexe($sexe);

            $em->persist($bachelier);


            // Extraire les données du bac :
            $moyenneBac = $sheet->getCellByColumnAndRow(10,$startRow)->getValue();
            $typeBac = $sheet->getCellByColumnAndRow(8,$startRow)->getValue();
            $sessionBac = $sheet->getCellByColumnAndRow(13,$startRow)->getValue();
            $mentionBac = $sheet->getCellByColumnAndRow(11,$startRow)->getValue();
            $etabBac = $sheet->getCellByColumnAndRow(14,$startRow)->getValue();


            $typeBacObj = $em->getRepository('PFASIGBundle:TypeBac')->find($typeBac);
            $sessionBacObj = $em->getRepository('PFASIGBundle:SessionBac')->find($sessionBac);
            $mentionBacObj = $em->getRepository('PFASIGBundle:MentionBac')->find($mentionBac);
            $etabBacObj = $em->getRepository('PFASIGBundle:EtablissementBac')->find($etabBac);

            // Création de l'établissement du Bac
            if($etabBacObj == null ){
                // Type de l'établissement du Bac
                $typeEtabBac = $sheet->getCellByColumnAndRow(16,$startRow)->getValue();
                $typeEtabBacObj = $em->getRepository('PFASIGBundle:TypeEtablissementBac')->find($typeEtabBac);

                // La délégation de l'établissement du Bac
                $delegBac = $sheet->getCellByColumnAndRow(19,$startRow)->getValue();
                $delegBacObj = $em->getRepository('PFASIGBundle:Delegation')->find($delegBac);

                $etabBacObj = new EtablissementBac();
                $etabBacLibelle = $sheet->getCellByColumnAndRow(15,$startRow)->getValue();

                $etabBacObj->setCodeLieu($etabBac)
                           ->setNomLieu($etabBacLibelle)
                           ->setDelegation($delegBac)
                           ->setTypeEtabBac($typeEtabBac);

                $em->persist($etabBacObj);

            }

            // Création du bac
            $bac = new Bac();
            $bac->setAnneeBac($anneObj)
                ->setBachelier($bachelier)
                ->setEtabBac($etabBacObj)
                ->setMentionBac($mentionBacObj)
                ->setMoyenne($moyenneBac)
                ->setSessionBac($sessionBac)
                ->setTypeBac($typeBacObj);

            $em->persist($bac);

        }


        $msg = "Les " + $startRow + " bacheliers ont été importés avec succès.";

        try{
            $em->flush();

        }catch(\Doctrine\DBAL\DBALException $e){
            $msg = "Les données n'ont pas pu être importées ! Une erreur est survenue !";

        }

        return $this->render('PFASIGBundle:Carte:bachelier.html.twig',array('msg' => $msg));

    }

РЕДАКТИРОВАТЬ 2: После применения рекомендаций от Марка, вот код, чтобы проверить:

public function bachelierAction(){

        $inputFileType = 'Excel2007';
        $inputFileName = 'Excel_Files/Bacheliers/Bacheliers12.xlsx';

        // Cell caching ::
        $cacheMethod = \PHPExcel_CachedObjectStorageFactory::cache_to_apc;
        $cacheSettings = array( 'cacheTime' => 600);
        \PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);


        /**  Create a new Reader of the type defined in $inputFileType  **/
        $objReader = \PHPExcel_IOFactory::createReader($inputFileType);
        $objReader->setReadDataOnly(true);
        /**  Define how many rows we want to read for each "chunk"  **/
        $chunkSize = 1;
        /**  Create a new Instance of our Read Filter  **/
        $chunkFilter = new chunkReadFilter();

        /**  Tell the Reader that we want to use the Read Filter  **/
        $objReader->setReadFilter($chunkFilter);
        $objPHPExcel = $objReader->load($inputFileName);

        // The Entity Manager :
        $em = $this->getDoctrine()->getManager();

        $anneObj = new Annee();
        $anneObj->setAnnee(2012);


        for ($startRow = 2; $startRow <= 6 ; $startRow += $chunkSize) {
            /*  Tell the Read Filter which rows we want this iteration  */
            $chunkFilter->setRows($startRow,$chunkSize);
            /**  Load only the rows that match our filter  **/
            $objPHPExcel = $objReader->load($inputFileName);
            $sheet = $objPHPExcel->getSheet(0);

            // Extraire les données du bachelier :
            list( ,$cne,$nom,$prenom, ,$sexe,$dateNaissance) = $sheet->rangeToArray('A'.$startRow.':F'.$startRow);
        }

        return $this->render('PFASIGBundle:Carte:bachelier.html.twig',array('msg' => $cne));

    }

К сожалению, я все еще получаю ошибку:FatalErrorException: Error: Allowed memory size of 157286400 bytes exhausted (tried to allocate 111967106 bytes) in C:\wamp\www\symf_PFA_v1\vendor\codeplex\phpexcel\PHPExcel\Reader\Excel2007.php line 428

Для записи, в моем php.ini я получил:

memory_limit=150Mapc.shm_size=180M

РЕДАКТИРОВАТЬ 3: Вот текущий код (улучшено)

public function bachelierAction(){

        $inputFileType = 'Excel2007';
        $inputFileName = 'myfile.xlsx';

        // Cell caching ::
        $cacheMethod = \PHPExcel_CachedObjectStorageFactory::cache_to_apc;
        $cacheSettings = array( 'cacheTime' => 600);
        \PHPExcel_Settings::setCacheStorageMethod($cacheMethod, $cacheSettings);

        /**  Create a new Reader of the type defined in $inputFileType  **/
        $objReader = \PHPExcel_IOFactory::createReader($inputFileType);
        $objReader->setReadDataOnly(true);
        /**  Define how many rows we want to read for each "chunk"  **/
        $chunkSize = 10;
        /**  Create a new Instance of our Read Filter  **/
        $chunkFilter = new chunkReadFilter();

        /**  Tell the Reader that we want to use the Read Filter  **/
        $objReader->setReadFilter($chunkFilter);
        $objPHPExcel = $objReader->load($inputFileName);


        $sheet = $objPHPExcel->setActiveSheetIndex(0);

for ($startRow = 2; $startRow <= 6 ; $startRow += $chunkSize) {
            /*  Tell the Read Filter which rows we want this iteration  */
            $chunkFilter->setRows($startRow,$chunkSize);
            /**  Load only the rows that match our filter  **/
            $objPHPExcel = $objReader->load($inputFileName);
            $sheet = $objPHPExcel->setActiveSheetIndex(0);

            $result = $sheet->rangeToArray('A'.$startRow.':G'.$startRow);
            list( ,$cne,$nom,$prenom, ,$sexe,$dateNaissance) = $result[0];

            // some process
            $result = $sheet->rangeToArray('I'.$startRow.':O'.$startRow);
            list($typeBac,,$moyenneBac,$mentionBac,,$sessionBac,$etabBac) = $result[0]; 

            // some process

        }
                // render the Twig template
}

класс chunkReadFilter:

namespace Me\MyBundle\Entity;

/**  Define a Read Filter class 
 * implementing PHPExcel_Reader_IReadFilter  */


class chunkReadFilter implements \PHPExcel_Reader_IReadFilter 
{ 
    private $_startRow = 0; 
    private $_endRow   = 0; 

    /**  Set the list of rows that we want to read  */ 
    public function setRows($startRow, $chunkSize) { 
        $this->_startRow = $startRow; 
        $this->_endRow   = $startRow + $chunkSize; 
    } 

    public function readCell($column, $row, $worksheetName = '') { 
        //  Only read the heading row, and the configured rows 
        if (($row == 1) ||
            ($row >= $this->_startRow && $row < $this->_endRow)) { 
            return true; 
        } 
        return false; 
    } 
} 

1 ответ

Решение

Наблюдение № 1

Воспользуйтесь преимуществом метода rangeToArray () PHPExcel вместо того, чтобы читать каждую ячейку по отдельности, используя getCellByColumnAndRow().

например

list ($cne, $nom, $prenom, , $sexe, $dateNaissance) = 
    $sheet->rangeToArray('A'.$startRow.':F'.$startRow);

Наблюдение № 2

Используйте кеширование ячеек вместо чанкинга через фильтр чтения, так что вы не будете постоянно перечитывать весь файл для каждого чанка, но сохраняете память

Наблюдение № 3

В качестве альтернативы, используйте немного больший размер куска, чем 1 строка за раз. Если у вас 100 строк, вам нужно перечитать весь файл 100 раз; установите размер фрагмента равным 10, и вам нужно только перечитать его 10 раз; установите так высоко, как позволит память.

Наблюдение № 4

Объедините наблюдения № 1, № 2 и № 3 с небольшим количеством экспериментов, чтобы увидеть, какой размер куска и методы кэширования обеспечивают наилучший баланс памяти и скорости.

Другие вопросы по тегам