Лучшие практики для перебора MASSIVE CSV файлов в PHP - PullRequest
7 голосов
/ 11 мая 2009

Хорошо, я постараюсь держать это коротким, сладким и точным.

Мы производим масштабные обновления GeoIP в нашей системе, загружая файл MASSIVE CSV в нашу CMS на основе PHP. Эта вещь обычно имеет более 100 тысяч записей информации об IP-адресах. Теперь простой импорт этих данных не является проблемой, но мы должны выполнить проверки на соответствие текущим региональным сопоставлениям IP-адресов.

Это означает, что мы должны проверить данные, сравнить и разделить перекрывающийся IP-адрес и т. Д. И эти проверки должны быть выполнены для каждой записи.

Не только это, но я только что создал решение для отображения полей, которое позволило бы другим поставщикам реализовывать свои обновления GeoIP в различных форматах. Это делается путем применения правил к записям IP-адресов в обновлении CSV.

Например, правило может выглядеть так:

если 'countryName' == 'Австралия', отправьте в 'Австралийский пул IP-адресов'

Возможно, необходимо выполнить несколько правил, и каждая запись IP должна применять их все. Например, 100 тыс. Записей для проверки по 10 правилам составят 1 миллион итераций; не весело.

Мы находим, что 2 правила для записей 100 КБ занимают до 10 минут. Я полностью осознаю узкое место здесь, которое представляет собой количество итераций сдвига, которое должно произойти для успешного импорта; просто не в полной мере осведомлены о каких-либо других возможностях, которые нам, возможно, придется немного ускорить.

Кто-то рекомендовал разбивать файл на куски на стороне сервера. Я не думаю, что это жизнеспособное решение, поскольку оно добавляет еще один уровень сложности к уже сложной системе. Файл должен быть открыт, проанализирован и разделен. Затем сценарий также должен был бы перебирать фрагменты.

Итак, вопрос, учитывая то, что я только что написал, каким будет ЛУЧШИЙ метод, чтобы немного ускорить этот процесс? Обновление аппаратного обеспечения сервера JUST для этого инструмента, к сожалению, не вариант, но для начала это довольно мощные устройства.

Не так коротко, как я думал, но да. Halps? (

Ответы [ 7 ]

12 голосов
/ 11 мая 2009

Выполните БОЛЬШОЙ ИМПОРТ в базу данных (то, что я использую SQL Server). ОБЫЧНЫЙ ИМПОРТ буквально занимает несколько секунд, и 100 000 записей - это арахис для базы данных, которая должна быть основана на бизнес-правилах. Я регулярно выполняю аналогичные обработки данных для таблицы с более чем 4 миллионами строк, и это не займет те 10 минут, которые вы перечислили.

РЕДАКТИРОВАТЬ: я должен отметить, да, я не рекомендую PHP для этого. Вы имеете дело с необработанными данными, используйте базу данных ..: P

1 голос
/ 11 мая 2009

100 тыс. Записей не большое количество. 10 минут - неплохое время для обработки одного потока. Объем необработанной работы, выполняемой по прямой линии, составляет, вероятно, около 10 минут, независимо от того, используете ли вы PHP или C. Если вы хотите, чтобы это было быстрее, вам понадобится более сложное решение, чем цикл while ,

Вот как бы я занялся этим:

  1. Используйте решение «карта / уменьшение» для параллельного запуска процесса. Hadoop, вероятно, излишним. Свинья латынь может сделать работу. Вы действительно хотите, чтобы карта была частью карты / уменьшить проблему. IE: вы разветвляете часть файла для обработки подпроцессом. Ваш редуктор, вероятно, cat. Простая версия могла бы иметь процессы PHP-форка для каждого фрагмента записи 10K, подождать потомков, а затем снова собрать их вывод.
  2. Использовать модель обработки очереди / сетки. Поставьте в очередь куски файла, затем зарегистрируйте кластер машин, получая задания и отправляя данные куда-нибудь. Это очень похоже на модель карты / уменьшения, только с использованием различных технологий, плюс вы можете масштабировать, добавляя больше машин в сетку.
  3. Если вы можете написать свою логику как SQL, сделайте это в базе данных. Я бы избегал этого, потому что большинство веб-программистов не могут работать с SQL на этом уровне. Кроме того, SQL является своего рода ограниченным для таких вещей, как проверки RBL или поиск ARIN.
1 голос
/ 11 мая 2009

Простым ключом к этому является сохранение как можно большей работы во внутреннем цикле.

Проще говоря, все, что вы делаете во внутреннем цикле, делается «100K раз», поэтому лучше ничего не делать (но, конечно, не практично), поэтому следующий лучший выбор - сделать как можно меньше.

Если у вас есть память, например, и это практично для приложения, отложите любой «вывод» до окончания основной обработки. Кэшируйте любые входные данные, если это возможно. Это лучше всего подходит для сводных данных или случайных данных.

В идеале, сохранить для чтения файла CSV, делать как можно меньше операций ввода-вывода во время основной обработки.

Предоставляет ли PHP какой-либо доступ к средству Unix mmap, которое, как правило, является самым быстрым способом чтения файлов, особенно больших файлов.

Еще одним соображением является пакетирование ваших вкладышей. Например, очень просто создать операторы INSERT в виде простых строк и отправить их на сервер в блоках по 10, 50 или 100 строк. Большинство баз данных имеют жесткое ограничение на размер оператора SQL (например, 64 КБ или что-то в этом роде), так что вам нужно помнить об этом. Это значительно сократит ваши поездки в БД.

Если вы создаете первичные ключи с помощью простых приращений, делайте это массово (блоки по 1000, 10000 и т. Д.). Это еще одна вещь, которую вы можете удалить из своего внутреннего цикла.

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

0 голосов
/ 16 апреля 2013

Я работаю с чем-то похожим.

CSV-файл, с которым я работаю, содержит португальские данные (дд / мм / гггг), которые мне нужно преобразовать в mysql гггг-мм-дд. Португальский денежный перевод: 1.000,15 руб. Обрежьте возможные пробелы и, наконец, добавьте косую черту.

Перед вставкой необходимо обработать 25 переменных.

Если я проверяю каждое значение $ notafiscal (выберите в таблице, чтобы увидеть, существует ли и обновите), php обрабатывает около 60 тыс. Строк. Но если я не проверю это, php обработает более 1 миллиона строк.

Сервер работает с памятью 4 ГБ - скриптовым локальным хостингом (память 2 ГБ), в обоих случаях он обрабатывает половину строк.

mysqli_query($db,"SET AUTOCOMMIT=0");
mysqli_query($db, "BEGIN");
mysqli_query($db, "SET FOREIGN_KEY_CHECKS = 0");
fgets($handle); //ignore the header line of csv file

while (($data = fgetcsv($handle, 100000, ';')) !== FALSE):
 //if $notafiscal lower than 1, ignore the record
 $notafiscal = $data[0];  
 if ($notafiscal < 1):
  continue;
 else:
  $serie = trim($data[1]); 
  $data_emissao = converteDataBR($data[2]);
  $cond_pagamento = trim(addslashes($data[3]));
  //...
  $valor_total = trim(moeda($data[24]));
  //check if the $notafiscal already exist, if so, update, else, insert into table
  $query = "SELECT * FROM venda WHERE notafiscal = ". $notafiscal ;
  $rs = mysqli_query($db, $query);
  if (mysqli_num_rows($rs) > 0):
    //UPDATE TABLE
  else:
    //INSERT INTO TABLE
  endif;
endwhile;

mysqli_query($db,"COMMIT");
mysqli_query($db,"SET AUTOCOMMIT=1");
mysqli_query($db,"SET FOREIGN_KEY_CHECKS = 1");
mysqli_close($db);    
0 голосов
/ 11 мая 2009

Работали над этой проблемой некоторое время. И, да, лучшим решением будет считывать только часть файла в любой момент времени, анализировать его, выполнять проверку, выполнять фильтрацию, затем экспортировать его и затем читать следующую часть файла. Я согласен, что это, вероятно, не решение для PHP, хотя вы, вероятно, можете сделать это в PHP. Пока у вас есть функция поиска, так что вы можете начать чтение из определенного места в файле. Вы правы, это добавляет более высокий уровень сложности, но стоит того, чтобы немного дополнительных усилий. Если ваши данные чистые, т. Е. Правильно разграничены, содержат строки, не содержат ломаных и т. Д., То все равно массовая загрузка в базу данных SQL В противном случае вы хотите знать, где, когда и почему возникают ошибки, и уметь их устранять.

0 голосов
/ 11 мая 2009

Если вы используете PHP для этой работы, переключите синтаксический анализ на Python, так как в этом случае он намного быстрее, чем PHP, этот обмен должен ускорить процесс на 75% или даже больше.

Если вы используете MySQL, вы также можете использовать оператор LOAD DATA INFILE, хотя я не уверен, что вам нужно проверить данные, прежде чем вставить их в базу данных.

0 голосов
/ 11 мая 2009

Одна вещь, которую вы можете попробовать, это запустить импорт CSV из командной строки PHP. Обычно это обеспечивает более быстрые результаты.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...