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=150M
apc.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 с небольшим количеством экспериментов, чтобы увидеть, какой размер куска и методы кэширования обеспечивают наилучший баланс памяти и скорости.