Pull to refresh

Comments 53

Ваш тест производительности опровергает ваши же утверждения о медлительности DOM и SimpleXML по сравнению с XMLReader. И еще бы не плохо показать объем используемой памяти.
да, последнюю цифру не правильно скопировал.
ок, добавлю данные по используемой памяти
тогда было бы неплохо и на CPU глянуть.
какими средствами лучше разбирать объёмный XML

Во первых надо отказаться от формата и переходить на json в любом случае.
Во вторых получать ленту со стороны сервера уже обработанную, по параметрам.
?part=snippet,contentDetails&maxResults=15&order=date
К сожалению наши поставщики прайс-листов не хотят переходить на параметры и отдают нам XML от 500 до 1500 Mb.
В принципе даже simplexml_load_file можете разбить на блоки, как вот товарищ пишет, он через curl/
Но вот как обрабатывает ошибки json.

Есть же какие то программы, которые конвертируют файл на стороне клиента, только конвертация уменьшит его размер на треть.
$json = curl_exec($ch);
  if ($json !== false) { 
//решаем проблему ошибок
$json = preg_replace("#(/\*([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*+/)|([\s\t]//.*)|(^//.*)#", '', $json);
setlocale(LC_ALL, 'ru_RU.utf8');
 Header("Content-Type: text/html;charset=UTF-8");
 curl_close($ch);
 
$json = json_decode($json, true) ; 

Не так давно пришлось изменять целый модуль после старого программиста, именно из-за его идеи разбивать файл на кучу новых по 30 Mb и разбирать всё это дело через simplexml. Такой утечки памяти добился он что сервер не мог выдержать 100 человек онлайн.
Можно парсить на сущности (теги), используя XMLReader, а затем эту сущность разбирать уже через DomDocument. Таким образом, памяти будет кушать не много, а DomDocument куда более приятен и расширяем, хотя, конечно, по скорости будет чуть медленнее.

А вообще, такие парсеры лучше писать на более шустрых комилируемых языках, к примеру go. Там это в разы будет быстрее работать и можно использовать конкурентные вычисления, писать порциями напрямую в базу. Вы удивитесь насколько это будет шустрее работать и памяти жрать будет в разы меньше
Ну так я и пишу, в принципе, если очень надо. XMLлом собаку уже съел, искренне не понимаю, кто наминусовал за «Во первых надо отказаться от формата и переходить на json в любом случае.».

Какие то странные кадры пошли в хабрахабре.

curl тут прописал, ибо раз 5 спрашивали в тостере как сделать чтобы кодировка была русская и ошибок не было. Им удобно смотреть и разбирать.

UFO just landed and posted this here
UFO just landed and posted this here
Возможно тем, что поставщики его не предоставляют?
А как можно обработать большой json (к примеру > 100 мб)? Если вот нужно, и никак иначе? Буду признателен за ответ

В java как-то помнится тупо использовал gson.

<старая шутка про java и память/>
использовать streaming json parser
Например: https://www.mkyong.com/java/jackson-streaming-api-to-read-and-write-json/
Если JSON вида [{}, {}, {}, ...] или похожий, где структура несложная, но есть большой кусок, который надо обрабатывать потоком, можно сделать так. Читаем посимвольно с учетом вложенности скобок, то есть определяем ситуации «начало массива», «конец массива», «начало объекта», «конец объекта». Задаем уровень вложенности или ключ, которые определяют одну единицу данных. Читаем текст для этой единицы в буфер, буфер передаем в json_decode(). Делаем обработку результата, читаем дальше.
И снова гуглинг по словам «JSON stream php» может навести на мысль что велосипедить — это тратить время.

Помимо времени выполнения следовало бы замерить использованную память.

У меня тоже есть поставщики фидов по 2-4 gb на xml

Мне пришлось написать безумные вещь для этой задачи.

— Открываю файл и читаю его построчно, пропуская открытие и закрытие, беру около 300 000 строк
— При помощи CURL рассылаю строки на эту же машину + на удаленные машины (24 запроса = 300 000/24)
— Все 24 запроса конвертируют XML в key/value массив
— Система собирает с каждого curl один массив

И так по кругу пока не кончится файл

Из сложного — надо правильно открывать и закрывать xml, чтобы он был валидный.

PS.
24 — это не сервера, это запросы, сервернов 4 по 6 (6 ядрен * на 4 офисные машины)

Проверьте htop, ваш php для этой задачи берет скорее всего только 1 ядро, заставить брать все ядра можно мультизапросами.
Уточняю

— Все 24 запрсоа не только конвертируют XML -> В массив, естественно они сначала конвертируют строки в XML объект. Но так как по XML объект ходить сложно (особенно, когда структура постоянно разная), то я написал функцию конвертации всех параметрво и аттрибутов в key/value массив, который возращается как json

— 24, 300 000 — все эти цифры условные, постоянно перенастраиваются и тд
Почитайте мой верхний комментарий.
Из-за таких способов происходит дикая утечка памяти, т.к. вы грузите по 300 000 строк, в то время когда воспользовавшись XMLReader или xml_parser вы будете тратить максимум 1 мегабайт памяти.
P.S. на разбор файла в 1.5 Gb уходит 7 Mb памяти и около 1.5 часа. Это учитывая что кроме разбора файла идёт множество mysql запросов
За что минусуют не могу понять, у меня на разбор уходет от 5 до 15 минут на 7gb xml данных, а у вас 1.5 часа на 1.5

Дикая утечка памяти? Авто же жаловался на скорость, зачем мне экономить память, и это не утечка, а использование. Моя задача скорость и я разбил файл на 300 000 кусков и разослал на обработку 6 серверам всем их ядрам под 100%.

Зачем это делать на сервере с онлайн пользователями — я не понимаю )

XMLReader или xml_parser — я жду их выполнения, что мне толку от их потребления памяти, памяти в избытке, а 300 000 строк по кругу не позволяют ей выйти из того объема, который меня устраивает. Если их же вызвать 24 раза одновременно — будет в 24 раза быстрее, а 300 000/24 — парсер не каждую строчку конвертирует, а все строчки как 1 файл на ядре.

А завтра поменяют разбиение XML-файла на строки и все у вас упадет.

Нет, я не конвертирую строки, я на 1 ядре объединяю их и делаю из них валидный xml, потому не важно как именно разбит файл, идея считывать группу строк, а не весь файл целиком.

Все упадет если файл будет 1 строковый. Ну под это тоже можно написать альтернативу, но по задаче не требуется.

А как вы будете разбивать файл вот такого формата?


<root><item>
    <name>Item 1</name>
</item><item>
    <name>Item 2</name>
</item><item>
    <name>Item 3</name>
</item><item>
    <name>Item 4</name>
</item><item>
    <name>Item 5</name>
</item></root>
По строкам, а потом допишу в разрыве xml, чтобы превратить в валидный, чтобы не потерять данные, следующий цикл начнется на пару строк выше разрыва, есть вероятность повтороного попадания данных (дубли), но они пропускаются при импорте

Нужно просто очистить хвост, чтобы в конце остался только закрывающийся тег
А завтра поменяют разбиение XML-файла на строки и все у вас упадет.
Вы это уже говорили, я не знаю как бы вам внятно объяснить. Есть открывающие и закрывающие теги, какая разница что там со строками и где что лежит, после получения данных для одного ядра строки соединяются и проверяются на валидность, все обрывистые куски отсеиваются и закрываются те теги, которые нужны, затем передается в xml reader

Как вы собираетесь отслеживать закрывающие тэги </item> второго уровня в файле произвольного формата?


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

Это время и память на работу на 1 сервере.
По факту вы делаете кучу лишней работы которая не нужна.
Возможно конкретно в вашем случае это помогло решить какие-то другие задачи, но в целом даже на обработке 300 000 строк я бы использовал xml_reader, а не simplexml
Ну это моя проблема выбора, конвертора, я лишь описал как я ускорил, если xml_reader лучше, то в данном случае он тоже отработает хорошо.

Да, заметил, набежит группа и минусует одновременно. Выбивают в других комментариях, а не по теме срача, кто хоть что то знает, а остаются полит. психи…
Скоро будет жежечка вторая.

Может нервничают от старого железа или перехода на PHP7, где SimpleXML extension были проблемы с установкой.

99% будет ошибка где то в XML, особо при таких гигантских файлах, не пойму как они работают.

Долго. Хотя, если такое время выполнения никому неудобств не доставляет — ну ладно.
Но, в любом случае, насилие БД… Вы при разборе каждого узла делаете запросы? У вас при разборе файла какие запросы превалируют: INSERT или UPDATE? Что, если собирать данные и отправлять их в БД пачками (bulk insert), т.е., вместо 100/200/500 запросов сделать 1? Или возможно, если вы используете innodb и у вас autocommit=1 (по умолчанию), а вы и не знали?
Оптимизация базы проведена.
Используются транзакции.
Большие запросы вынесены в отдельный файл который работает с очередью RabbitMQ
Была задача распарсить многогиговые XML файлы, делал очень просто, зная структуру XML файла читал построчно, последовательно заполняя и обрабатывая объектную модель. При незнакомом формате, можно в два прохода, на первом строит схему XML, на втором уже зная ее обрабатываем последовательно блоки.
А что, регулярками уже никто не парсит? :)
Кстати, вполне вариант для файла вида
<root><item>
    <name>Item 1</name>
</item> ... </root>

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

Еще придумался такой способ (не для всех типов данных) — конвертировать снаружи XML в нечто табличное (CSV / TSV), через XSLT, а затем вычитывать построчно (или тот же MySQL может такое съесть через LOAD DATA INFILE). Да, колонок будет овер много в общем случае. Конвертировать можно например не в файл, а в пайп, чтоб место поберечь на диске.
Ну почему же никто :)
Тоже была задача в своё время распарсить xml на сотню-другую мегабайт. При этом структура была заранее известна и размер item-а был вменяемый, не больше сотен килобайт.
Комбинация регулярок с построчным чтением файла вполне спасала от перерасхода памяти.
Читаю и ужасаюсь — чего только не придумают люди, у которых нет StAX
Хм, а у меня xml_parse был быстрее на порядок. :)
Уф… XML в несколько гигов это жесть)) Мне пока максимум доводилось работать с 30-35Мб, использую Simple XML и пару раз DOM за карьеру. Про другие библиотеки не знал, спасибо.
Вы не пробовали использовать xslt преобразования? С их помощью из многомегабайтного XML документа можно вычленить только интересующую вас информацию, а уже её зачитывать в сущности.
У нас вся интересующая информация в xml )
Именно весь xml и нужен был.
По запросу «php stream xml» вроде бы в первых же результатах вылезают ссылки на XMLReader, SAX и другие полезные слова?
(это коммент не к статье, а к товарищам, которые велосипедят)
но почему-то до сих пор 80% <=middle разработчиков используют simplexml )
С каких пор использование simplexml — вилосипедописание?
велосипедописание если идет работа с файлом более 50 Mb
UFO just landed and posted this here
UFO just landed and posted this here
Как насчет использования eXist? Он как раз и создавался для удобства в оперировании большими XML файлами.
Используйте SAX парсер, но не используйте его главную фишку — парсить в потоке?
У вас поэтому такая смешная разница между DOM и SAX.
Этот парсер легко переваривает многогиговые эксельки (любой zip архив с xml), которые офис не в состоянии открыть.
https://www.sitepoint.com/functional-programming-phunkie-building-php-json-parser/
— A JSON Parser.
По опыту, большее количество кода это обнаружение и устранение ошибок, вот как работает по новому. См. выше.
Sign up to leave a comment.

Articles