Odczyt danych z dużego XML do tablicy

0

Witam,

próbuję odczytać dane produktów otrzymanych w postaci linku do pliku xml. Stąd dotarłem do https://www.linkedin.com/pulse/parsing-huge-xml-files-php-bartosz-pacho%C5%82ek

Z tego artykułu wynika, że w moim przypadku najwydajniejsze (tak wywnioskowałem, aby oszczędzić zasoby) będzie wykorzystanie XMLReader + SimpleXML, więc:

ini_set('memory_limit', '-1');

$xml = new XMLReader;
$xml->open('https://url_to_xml.xml');
while ($xml->name !== 'product') {
    $xml->read();
}

$data = new stdClass();
$data->products = [];

$productIndex = 0;

do {
    $product = simplexml_load_string($xml->readOuterXml());
    $data->products[$productIndex]['id'] = $product->id;
    $data->products[$productIndex]['name'] = $product->name;
    // dalsze własności produktu
    $productIndex++;
} while ($xml->next('product'));
$xml->close();

Link, który otrzymałem jest do pliku XML - po pobraniu (nie będę go pobierał za każdym razem, więc forma zostaje jako URL) -
ma rozmiar ok. 400MB, a zawiera około 40.000 produktów.
Wykorzystując kod, w postaci wyżej podanej wciąż jest to czas ładowania rzędu 2 minut. Stąd nasuwa się myśl - jeśli będzie tych produktów nawet 500.000, plik będzie znacznie większych rozmiarów, czas się wydłuży.

Jak rozwiązujecie taką kwestię? Czy ten czas, przy takich rozmiarach i rekordach jest "w normie"? Czy coś jeszcze powinno zostać uwzględnione?

Pozdrawiam

1
inven napisał(a):

Jak rozwiązujecie taką kwestię? Czy ten czas, przy takich rozmiarach i rekordach jest "w normie"? Czy coś jeszcze powinno zostać uwzględnione?

2 min za 400MB to jest bardzo słaby czas.
Jak sam napisałeś masz w plik dużo "ITEMS" jakiegoś produktu. Czyli najprawdopodobniej jest to struktura płytka z dużą ilością elementów a nie głęboka.
Takie pliki zwykle wczytuje się sekwencyjnie po kawałku.
Ja zazwyczaj rozbieram plik na mniejsze części a potem same wydłubane ITEMS parsuję używając np.: SimpleXML

Musisz wykorzystać to, że znasz strukturę dokumentu i nie podchodzić do tego, że trzeba wczytać cały XML jednym "łyknięciem".

Kolejny problem jest taki, że przy większym pliku zwyczajnie zabraknie Ci pamięci czy to RAM czy wynikającej z ograniczeń PHP.

0

@inven:

Najważniejsze jest to, czego nie ma w tym fragmencie.
Co z tym potem robisz (zamiast prędziutko odłożyć np do bazy danych, i dać się zmiennym zrecyklować)
Lub drugie pytanie, po co tablica w przestrzeni skryptu.

W jakimkolwiek uniwersalnym języku bym to z całą pewnością robił poza przestrzenią serwera webowego, w jakimś programiku linii komendy.

0

Musisz mieć to wszystko w tablicy? Raczej nie wykonywałbym tego skryptu z poziomu przeglądarki tylko przez komendę + poczytaj o yield.
@katakrowa tak się robiło 20 lat temu

0

Oto przykładowy XML: https://pastebin.com/D1MY7his

katakrowa napisał(a):

[...]
Takie pliki zwykle wczytuje się sekwencyjnie po kawałku.
Ja zazwyczaj rozbieram plik na mniejsze części a potem same wydłubane ITEMS parsuję używając np.: SimpleXML

Musisz wykorzystać to, że znasz strukturę dokumentu i nie podchodzić do tego, że trzeba wczytać cały XML jednym "łyknięciem".

Kolejny problem jest taki, że przy większym pliku zwyczajnie zabraknie Ci pamięci czy to RAM czy wynikającej z ograniczeń PHP.

Jesteś w stanie przybliżyć mi temat sekwencyjnego odczytu pliku, jak to ugryźć?

ZrobieDobrze napisał(a):

@inven:

Najważniejsze jest to, czego nie ma w tym fragmencie.
Co z tym potem robisz (zamiast prędziutko odłożyć np do bazy danych, i dać się zmiennym zrecyklować)
Lub drugie pytanie, po co tablica w przestrzeni skryptu.

W jakimkolwiek uniwersalnym języku bym to z całą pewnością robił poza przestrzenią serwera webowego, w jakimś programiku linii komendy.

Dane zgromadzone w tablicy służą dalej do wyszukiwania odpowiednich np. produktów. XML zmienia się dynamicznie, więc bazę danych odpuściłem.

666 napisał(a):

Musisz mieć to wszystko w tablicy? Raczej nie wykonywałbym tego skryptu z poziomu przeglądarki tylko przez komendę + poczytaj o yield.
@katakrowa tak się robiło 20 lat temu

jak wyżej - xml zmienia się, więc wykorzystałem tablicę. Są lepsze opcje?

Z yield'em nigdy się nie spotkałem, wykorzystując https://gist.github.com/arth2o/5606b960e29bb9b8df61 wydłuża ładowanie, a nawet zawiesza.

3

Ty tu właśnie powinieneś użyć bazy danych. Sekwencyjne aktualizować dane z XML do DB co jakiś kwant czasu. A wszelkie operacje wykonywać zapytaniami do DB.

2
inven napisał(a):

Jesteś w stanie przybliżyć mi temat sekwencyjnego odczytu pliku, jak to ugryźć?

Zapoznaj się z funkcjami:
Wersja najłatwiejsza to

Możesz to też zrobić na strumieniach:
Gotowy przykład: klink

Dane zgromadzone w tablicy służą dalej do wyszukiwania odpowiednich np. produktów. XML zmienia się dynamicznie, więc bazę danych odpuściłem.

a zdecydowanie nie powinieneś.
Lepiej często aktualizować bazę z boku na podstawie tego XML niż robić kombinacje o jakich piszesz. Sama aktualizacja może być wykonywana tylko w sytuacji gdy zmieni się np. MD5 pliku źródłowego.
Nie musisz jej robić przed każdym wyszukiwaniem.

Zaleta jest taka, że w procesie ładowania i wyszukiwania nie musisz mieć wszystkich danych w pamięci. Baza na 100% będzie wyszukiwać lepiej niż Twój nieistniejący jeszcze program. Do tego będziesz mógł sortować, grupować, łączyć i robić wiele innych cudów...

0

Dziękuję za odpowiedzi. Najbliżej jest mi do MySQL, stąd też biorę się za przepisywanie programu. Wnioskując po Waszych odpowiedziach - insert do bazy z każdym wykonaniem pętli.
A jak z aktualizowaniem? Ponownie iteracja po każdym elemencie XML, porównując ich dane (czy istnieje w bazie, a XML nie i odwrotnie), czy wyczyścić tabelę i wrzucić rekordy na nowo? Jak będzie najoptymalniej?

Pozdrawiam

0

Iterujesz po każdym elemencie. Ustawiasz klucz np na id produktu i stosujesz ten trik https://stackoverflow.com/questions/4205181/insert-into-a-mysql-table-or-update-if-exists

0

Wrócę jeszcze na chwilę do tematu.

Jeśli byłoby (zakładam) 10 użytkowników, każdy ma swoje produkty, czyli powiedzmy każdy z nich ma po około 200.000. W takim wypadku byłoby 2.000.000 w tabeli, a pewnie wyjdzie dużo więcej. Jak to się ma do wydajności i ograniczeń baz danych? Czy dalej w nią brnąć?

Te pytania powinny wyczerpać już mój temat.

Pozdrawiam

0

2M rekordów to nie jest jakaś gigantyczna ilość. Dużo zależy jak poukładasz sobie dane w tabelach i na jakim sprzęcie to będzie pracować.

0

@inven:

jesli miało być pejoratywne "brnąć", to właśnie te własne wydumki.
Bazy do tego są, aby trzymać dane, i robią to dobrze (o ile nie spieprzyć projektu: relacje, indeksy). 2 miliony to baza mała, jakbyś wyliczył 20mln rek, to najwyżej średnia.

Na bieda hostingach o wiele szybciej trafisz na limit RAM dla PHP, niż nawet w najniższych ofertach bazy.

2

Ogólnie tworzenie tablic w PHP nie jest bardzo wydajne. Alokowanie dużych danych jest względnie dłuższe niż w innych dynamicznie typowanych językach jak python czy ruby.

Zauważyłem to jak developowałem libke T-Regx, żeby sprawdzić czy nie nakłada jakichś dużych overheadów przy dużych matchach. Myślałem że biblioteka albo sam php nie wydała z ogarnianiem regexpów, ale po tygodniu rozkmin okazało się że sama alokacja dużego array'a była problemem. Przerobienie arraya na generator naprawiło problem.

1 użytkowników online, w tym zalogowanych: 0, gości: 1