Оптимизация сценариев командной строки php для обработки больших плоских файлов

0

Для фейри вниз. Я знаю, что php - это неправильный язык для этого... но я работаю под внешними ограничениями. Учитывая, что:

У меня есть большой плоский файл, который мне нужно обрабатывать в php. Я конвертирую плоский файл в нормализованную базу данных в mysql. В плоском файле имеется несколько миллионов строк.

Первоначально я пытался использовать систему ORM при импорте плоского файла. С этой конструкцией возникла серьезная проблема с утечкой памяти php даже при тщательном освобождении объектов. Даже если я гарантирую, что хватит памяти, script потребуется около 25 дней для работы на моем рабочем столе.

Я удалил служебные данные и переписал script, чтобы напрямую создавать команды mysql. Я удалил AUTO INCREMENT из моего дизайна, так как это потребовало от меня, как Mysql, что последний идентификатор был введен для установления отношений между точками данных. Я просто использую глобальный счетчик для идентификаторов базы данных, и я никогда не занимаюсь поиском, просто вставляет.

Я использую команду split unix для создания большого количества небольших файлов вместо одного большого, потому что есть лишние ресурсы памяти, связанные с использованием указателя файла снова и снова.

Используя эти оптимизации (надеюсь, они помогут кому-то еще), я получил импорт script для запуска примерно через 6 часов.

Я арендовал виртуальный экземпляр с 5-кратной ОЗУ и примерно в 5 раз больше мощности процессора, чем мой рабочий стол, и заметил, что он прошел точно такую ​​же скорость. Сервер запускает процесс, но имеет запасные циклы процессора и оперативную память. Возможно, ограничивающим фактором является скорость диска. Но у меня много ОЗУ. Должен ли я как-то загружать файлы в память? Любые предложения по дальнейшей оптимизации скриптов командной строки php, обрабатывающих большие файлы, приветствуются!

  • 0
    Просто упомяну, что еще я сделал, чтобы включить индексирование всех таблиц перед запуском сценария импорта. Поскольку существует много транзакций INSERT, вы можете не допустить, чтобы база данных касалась нескольких индексов на таблицу для каждой вставки. Это экономит уйму времени, особенно потому, что мне нужно создать много неформализованных высокоиндексированных таблиц, чтобы иметь возможность быстро запрашивать полученную базу данных монстров.
Теги:

5 ответов

3
Лучший ответ

Вам это не понравится, но... похоже, что вы используете неправильный язык для задачи. Если вы хотите получить некоторые скачки в скорости, то следующий шаг будет идти на скомпилированный язык. Скомпилированные языки работают намного быстрее, чем язык сценариев, поэтому вы увидите, что время обработки сокращается.

Кроме того, вы можете сбросить данные в БД с помощью команды сборки. У Postgres был один (Dump? Load? Что-то вроде этого), который будет читаться в текстовом файле с разделителями табуляции, столбцы которого сопоставляются с столбцами в таблице. Это позволит вам просто сосредоточиться на получении текстового файла в нужном формате, а затем выплескать его в БД с помощью одной команды и позволить ему обрабатывать оптимизацию, а не себя.

Вы сделали правильную вещь, столкнувшись с ORM на голове, разделение файла не нужно, хотя, поскольку ваш читатель текстовых файлов должен просто использовать буфер внутри, поэтому он "должен" неважно, но я не * nix парень, поэтому может быть неправильно на этом фронте.

Мы сделали что-то похожее с .net-приложением, которое каждое утро пропускает через 20 ГБ файлов, делая RegExp на каждой строке, сохраняет хэш-память в памяти для уникальных записей и затем выдает новые в БД. Из этого мы затем выплескиваем 9000+ JS файлов, используя Ruby Script для простоты (это самая медленная часть). Раньше у нас был импортер, написанный на Ruby, и все это заняло 3 часа, переписывание в .net запускает весь процесс примерно за 30-40 минут, а 20 из них - медленный Ruby Script (не стоит оптимизировать что, хотя это делает работу достаточно хорошо).

  • 0
    Грустно, но правда. Попробуйте импортировать его напрямую в postgresql (может выполнить эту операцию лучше, чем mysql) или использовать другой язык (python?)
  • 1
    Пятилетний вопрос набирал голоса, интересно, по какой причине? Догма? Всегда приятно знать.
3

Несколько важных рекомендаций по дизайну для такой задачи:

Не читайте весь файл сразу в памяти. Используйте указатель файла и читайте в разумных кусках (скажем, несколько килобайт.. зависит от среднего размера записи). Затем обрабатывайте каждую запись и выгружайте буфер. Я не уверен в вашем описании, делаете ли вы это сейчас или нет.

Если ваш тип хранилища mysql поддерживает транзакции (таблица должна быть InnoDB), вы можете использовать их для оптимизации. Начать транзакцию и процесс f.ex. 100 тыс. Строк, затем выполнить флеш, совершив транзакцию и открыв новую. Это работает, потому что MySql будет обновлять индекс только один раз, а не для каждой строки.

Другой вариант - использовать объемную вставку. Если ваша база данных не является локальной (например, вы подключаетесь по сети), это может привести к повышению. Я думаю (не уверен, хотя) он также дает те же преимущества, что и транзакции - возможно, даже для таблиц MyIsam.

Наконец, если ничего не работает, вы можете удалить php из уравнения и использовать LOAD DATA INFILE. Возможно, вам придется предварительно обработать файл сначала, используя php или какой-либо другой язык обработки текста (awk или sed имеют очень хорошие профили производительности)

2

Время, которое вы тратите, похоже на разные машины, может быть связано с связью между PHP script и сервером MySQL: для каждого запроса MySQL:

  • вы создаете запрос в PHP script (зависит от мощности машины, но очень быстро)
  • вам необходимо отправить этот запрос на сервер MySQL (через сеть или локальный сокет; требуется время)
  • сервер MySQL должен иметь дело с данными (хранить их, создавать индексы, использовать блокировки для безопасности,...)
  • Ответ ( "ok", вставленные данные) должен вернуться к PHP (такой же: сеть или сокет, т.е. медленный)
  • и он делает все это каждый раз.

Что занимает время, вероятно, на самом деле не на стороне PHP; скорее всего, это между PHP и MySQL - и, возможно, вы ничего не сможете с этим сделать.

Если у вас достаточно мощная машина, я бы предложил следующее:

  • разделите ваши данные в X (не слишком человек, скажем, X = 6).
  • измените свой PHP script, чтобы вы могли запускать его 6 раз параллельно, указав в качестве параметра номер части, на которой он должен работать.
  • Запустите script 6 раз.

Первое параллельное выполнение script будет обрабатывать в 6 раз меньше данных; то же самое для других... И они будут работать параллельно... Итак, в конце концов, весь процесс займет, может быть, в 4 раза меньше времени: -)

Вероятно, это не займет в 6 раз меньше времени: работа с параллелизацией означает добавление некоторой нагрузки на машину и что MySQL будет иметь некоторые запросы в concurrency - но только с несколькими параллельными процессами, это будет хорошо.

В качестве побочного примечания: делать это с PHP может и не самое лучшее. Вот еще один способ, о котором я могу думать:

  • используйте script (например, PHP или Perl или оболочку или что-то еще), чтобы:
    • прочитать входные файлы
    • генерировать запросы на вставку (но не отправлять серверу MySQL)
    • пишите эти запросы в файл
  • когда весь запрос на миллионы строк находится в файле:
    • запустите, одним выстрелом, этот файл в MySQL.
    • что-то подобное в командной строке: "mysql --host=HOST --user=USER --password=PASSWORD DATABASE_NAME < inserts-commands.sql"

Таким образом, просто убедитесь, что SQL-запросы в файле в порядке, а затем MySQL импортирует все за один снимок: вам не нужно переходить с PHP на MySQL для каждого запроса - он должен идти очень быстро.

Надеюсь, это поможет, получайте удовольствие!

1

Кроме optmizations из script, вы предложите попробовать любой ускоритель PHP (например: eaccelerator.net). Если это не поможет, я бы предложил использовать язык/платформу, созданную для таких задач.

  • 0
    Я не думаю, что это будет иметь большое значение. Эти ускорители работают за счет кэширования кодов операций, что означает, что время запуска сценариев сокращается. Это не имеет никакого значения для фактического времени выполнения.
  • 0
    eAccelerator включает в себя оптимизатор кода opt ... своего рода "pre-JIT" ... но я не понимаю, насколько это важно.
0

Как почти каждый другой ответ указывает. PHP менее идеален для такого рода обработки.

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

Но кому-то, возможно, понадобится использовать PHP для обработки больших файлов, как я.

Учитывая это, я должен отметить, что новый инструмент Facebook xhprof отлично работает в командной строке.

Если вы отправите ему следующую команду:

xhprof_enable(XHPROF_FLAGS_CPU + XHPROF_FLAGS_MEMORY );

Вы можете легко увидеть, какой конкретный раздел вашего собственного кода занимает столько времени.

HTH, кто-то еще обречен на продолжение круговой привязки через квадратную привязку ко мне.

-Ft

Ещё вопросы

Сообщество Overcoder
Наверх
Меню