Работа с очень большим файлом

graf_vorontsov

пытаюсь обработать файл, и после загрузить данные полученные из него в БД
12 206 610 строк, вес 608 мб
http://php.net/memory-limit увеличил до 700МБ не помогает

ошибку такую выдаёт
Fatal error: Out of memory (allocated 460849152) (tried to allocate 460330898 bytes)

 

runcore

а что за файл?
в чем заключается обработка?

если текстовый — то построчно читать нужно
если XML то потоковый парсер используй

 

artoodetoo

В PHP специфическое внутреннее представление данных, с большим оверхедом. Особенно если ты грузишь файл в массив строк или объект с кучей полей. Каждое атомарное значение сопровождается еще внутренним дескриптором. Для массива добавляются издержки на индексы. В общем «скорость в обмен на память» — вот девиз PHP :)

Измени алгоритм так, чтобы не понадобилось хранить целиком. Обрабатывай в цикле по записям, чтоли.

 

graf_vorontsov

файл текстовый такого содержания

Код (Text):
  1. ‘325311’,’9211001G60′,’3′,’558′,’9211001G60′,»,’1′,’1′
  2. ‘325311’,’9211001G61′,’3′,’558′,’9211001G61′,»,’2′,’1′
  3. ‘325311’,’946319′,’4′,’10191′,’94-6319′,»,’1′,’1′
  4. ‘325311’,’DN5184′,’4′,’742′,’DN5184′,»,’1′,’1′
  5. ‘325311’,’TSP0225346′,’4′,’89’,’TSP0225346′,»,’1′,’1′

данные получаю в массив и загружаю в бд

 

runcore

читайте построчно. вставлять в БД можно сразу, можно порциями, а можно писать готовые запросы в файл и получившийся «дамп» скармливать в БД с пом mysqldump. если юзаете мускул

 

graf_vorontsov

та и так циклом же прохожу файл… может его както программно можно разделить выполнение файла на части, например после 20 мб выгрузить файл из памяти и продолжить с того момента на котором остановился… :) не знаю.. придумал чтото нереальное :)

 

artoodetoo

Код (PHP):
  1. while (!feof($handle)) { 
  2.     $line = stream_get_line($handle, 1000000, «n«);
  3.     // полезная работа 
  4. } 

Добавлено спустя 1 минуту 29 секунд:
в цикле читать и писать по одной записи. а не «прочитать все и затем циклом по массиву».

 

runcore

да даже простого fgets($fh) хватит

 

artoodetoo

нет
то есть да, наверное. но та функция вроде быстрее в 100500 раз ))) судя по коментариям в офф. доке

 

runcore

?

 

artoodetoo

… и, главное, stream_get_line обрезает символ конца строки, а при fgets тебе самому надо об этом позаботиться.

 

graf_vorontsov

вот так считываю файл в массив

Код (Text):
  1.     function get_array($read_file){
  2.         while(($arr = fgetcsv($read_file, 100000000,»,»,»‘»))!== FALSE){
  3.         $massiv[] = array_merge($arr);
  4.         }
  5.     return $massiv;
  6.     }

ну а потом foreach…. и в бд полученые результаты

 

artoodetoo

Дык! У тебя почти всё готово. Совмести свои while и foreach в один цикл и выкини $massiv за ненадобностью.

 

graf_vorontsov

ну.. совместил…ничего не поменялось. маленький файл работает без проблем а большой пишет как и тогда

Код (Text):
  1.     $file = fopen(«cross.txt», «r»);
  2.    
  3.     while(($fle = fgetcsv($file, 100000000,»,»,»‘»))!== FALSE){
  4.    
  5.     foreach ($fle as $nums){
  6.         }
  7.        
  8.         $query = mysql_query(«INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES (‘$fle[0]’, ‘$fle[1]’, ‘$fle[3]’, ‘$fle[4]’)»);
  9.        
  10.     }
 

artoodetoo

Блин, ну ты чо! Один цикл нужен, а не вложенные циклы.
Насколько я понимаю, надо вот так:

Код (PHP):
  1. $file = fopen(«cross.txt», «r»);
  2. while(($fle = fgetcsv($file, 100000000,«,»,«‘»))!== FALSE){
  3.         $query = mysql_query(«INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES (‘$fle[0]‘, ‘$fle[1]‘, ‘$fle[3]‘, ‘$fle[4]‘)»);
  4. }
  5. fclose($file);
  6.  

Добавлено спустя 6 минут 15 секунд:
Кстати, я сейчас некоторые замеры провел. fgets просто чемпион по тормознутости! ))) Причем скорость зависит от длины строки, похоже он посимвольно читает без нормального буфера!

fgetcsv работает в разы быстрее, хотя делает ещё дополнительную работу по распарсиванию строки.

stream_get_line просто метеор! Но если добавить «ручное» распарсивание считанной строки, то общий результат конечно хуже чем fgetcsv. Вполне предсказуемо.

 

r3l0c

stream_get_contents+str_getcsv

Добавлено спустя 38 секунд:
ой, stream_get_line

Вообще кста все функции stream_ оч быстры)

 

Your

Памяти не хватает…
Апач не режит ничего? по Rlimit ?
Там зацепок уйма может возникнуть…

 

graf_vorontsov

работает быстро, читает построчно, но распарсить строку както не въеду как…

Код (Text):
  1. while (!feof($handle)) {
  2.         $line = stream_get_line($handle, 0);
  3.         $fle = str_replace(«/’/», «», explode(«,», $line));
  4.            
  5.         $query = mysql_query(«INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES (‘$fle[0]’, ‘$fle[1]’, ‘$fle[3]’, ‘$fle[4]’)»);
  6.     }

тут убрал вложеный массив но ошибка не исчезла

Код (Text):
  1.     while(($fle = fgetcsv($file, 100000000,»,»,»‘»))!== FALSE){
  2.    
  3.         $query = mysql_query(«INSERT INTO crossnumbers (ARL_ART_ID, ARL_SEARCH_NUMBER, ARL_BRA_ID, ARL_DISPLAY_NR) VALUES (‘$fle[0]’, ‘$fle[1]’, ‘$fle[3]’, ‘$fle[4]’)»);
  4.        
  5.     }
 

graf_vorontsov

так надеюсь будет работать… завтра попробую и отпишусь, файл на работе лежит
прийдётся PHP 5.3 на работе тоже поставить.. а то str_getcsv 5,2 не хавает :)

Код (Text):
  1. while (!feof($handle)) {
  2.         $line = stream_get_line($handle, 0, «rn»);
  3.         $fle = str_getcsv($line, «,», «»», «rn»);
  4.     }
 

runcore

хе хе
там в комментах просто ктото неумеет пользоваться правильно этими функциями.
фишка в том, что для fgets параметр $length является необязательным. если его не указывать то:
мне самому интересно стало.
неполенился сгенерил файл на 70000 строк ~ 7 Мбайт
затестил такой код. комментил внутри цикла сначала одну, потом другую функцию

Код (PHP):
  1. $fh = fopen(‘./bigfile.txt’, ‘r’);
  2. $t1 = microtime(1);
  3. while (!feof($fh)) {
  4.     $line = trim(fgets($fh)); // 0.063982009887695 сек.
  5.     //$line = stream_get_line($fh, 1000000, «n»); // 0.19860410690308 сек.
  6. }
  7. echo microtime(1)$t1;
  8. fclose($fh); 

fgets() быстрее читает раза в 2! даже вместе с trim(), а без неё раза в 3.

с stream_get_line() такой финт неполучится, ибо у нее параметр $length ОБЯЗАТЕЛЬНЫЙ. а если строки разной длинны читаем — то приходится указывать с запасом. вот она и начинает тормозить. максимум можно уменьшить его, но уже опасность что читать будем непострочно а хз как. но если известно сколько байт читать то будет паритет, ибо $length есть у ОБОИХ функций в параметрах.

в общем. я верю только официальной доке:

тоесть они аналогичны по скорости, НО у каждой есть свои особенности. в общем случае, при чтении текстового файла с разнородными строками(как у ТС), fgets() предпочтительнее, так как сама найдет перевод строки и быстрее. зато stream_get_line() более гибкая в настройке.

 

Добавить комментарий

Ваш адрес email не будет опубликован. Обязательные поля помечены *