Autor Zpráva
ForestCZE
Profil
Ahoj, řeším problém s parsováním. Potřebuju naparsovat cca 1300 xml souborů a poté to přidat do databáze. PHP to samozřejmě za 30s nezvládne (zvyšovat set_time_limit mi nepřijde úplně moudré). Mohu to parsování provést nějak asynchronně na pozadí, kde by to vložení do databáze počkalo na parsing a pak to vložilo? Snad jsem to napsal nějak srozumitelně. Děkuji předem za odpověď :)
mckay
Profil
A musíš to parsování provádět přímo na serveru? PHP jde pustit i z příkazové řádky a tam ten limit už potom neplatí. Dost tu záleží na tom jaký je tvůj případ použití. Pokud je to jednorázová záležitost, kterou potřebuješ udělat, půjde to snadno. Pokud to budeš dělat pravidelně na např. sdíleném webhostingu, bude potřeba více elaborátní řešení.

Pokud to děláš lokálně, tak přejdi do složky kde je nainstalované PHP, podsložka bin, podrž levý shift, klikni pravým, otevři terminál/commandlline a php cesta/k/scriptu.php.

Pokud ta databáze jen vzdálená a nemůžeš se k ní připojit že své stanice, můžeš ještě udělat to, že si na tom vzdáleném webu vytvoříš endpoint, který umožní vložit obsah jednoho naparsovaneho souboru (pokud to stihne pod 30s). Parsovat budeš pak zase lokálně a po každém naparsovanem souboru budeš volat ten endpoint pro uložení jeho obsahu.

Půjde to takto?
ForestCZE
Profil
mckay:
Děkuji za super odpověď :) Parsování a uložení do databáze těch cca 1300 souborů bude jednorázové vždycky pro jeden okruh těch souborů, pak už by to řešilo jen nové soubory nebo změny ve stávajících. Toto bych chtěl řešit CRONem, protože se to bude dít každých 24h a každou hodinu. Jestli to chápu dobře, tak pak bych se měl zbavit toho problému s limitem, jelikož to nepoběží přímo na webu. Vše provádím přes SSH na vzdáleném VPS. Teoreticky bych to mohl testovat rovnou na tom CRONu nebo přes web s menším počtem souborů, dokud to nebude dělat přesně to, co potřebuji? Pomůže ve finále ten CRON? Děkuji.
Keeehi
Profil
ForestCZE:
protože se to bude dít každých 24h a každou hodinu.
Takže vlastně každou hodinu.

Jestli to chápu dobře, tak pak bych se měl zbavit toho problému s limitem, jelikož to nepoběží přímo na webu.
Tak nějak. Když php script spustí cron, je to podobné jako když ho pustíš z příkazové řádky ručně. Takže tam by limit na dobu běhu být neměl. Říkat že to neběží na webu je takové neurčité. Ano, ten běh není důsledkem HTTP requestu, na druhou stranu je to to samé PHP a běží to na stejném serveru.

Vše provádím přes SSH na vzdáleném VPS.
Vyvíjet a testovat bys to měl vždy lokálně a na server nahrávat vždy až otestovanou verzi.

Teoreticky bych to mohl testovat ... s menším počtem souborů, dokud to nebude dělat přesně to, co potřebuji?
Skvělý nápad.
ForestCZE
Profil
Tak jsem se dostal k tomu, že to posílám z CLI. Je tu ale ještě jeden problém, se kterým si nevím rady.

Mám to teď následovně:

/**
     * Gets all records from a folder
     * @param string $feedDir Path to folder
     */
    public function getRecords(string $feedDir): array
    {
        $records = [];
        $open = opendir($this->path . $feedDir);
        while (($file = readdir($open)) !== false)
        {
            if ($file == "." || $file == "..") continue;
                $records[] = $this->path . $feedDir . $file;
        }
        closedir($open);
        return $records;
    }

/**
     * Adds parsed records to database
     * @param array $files Array of XML files
     */
    public function addParsedRecords(array $files): void
    {
        $records = [];
        for ($i = 0; $i < count($files); $i++)
        {
            $records[] = $this->XMLParser->parse($files[$i]);
        }
        $this->addRecordsToDB($records);
    }

/**
     * Adds record to database
     * @param array $records Array of records
     */
    private function addRecordsToDB(array $records): void
    {
        $rows = [];
        foreach ($records as $record)
        {
            $data = [];
            $data["country"] = $record->COUNTRY;
            $data["destination"] = $record->DESTINATION;
            $data["name"] = $record->NAME;
            $data["accommodation"] = $record->ACCOMMODATION;
            $data["category"] = $record->CATEGORY;
            $data["latitude"] = $record->MAP["LATITUDE"];
            $data["longitude"] = $record->MAP["LONGITUDE"];
            $data["description"] = $record->DESCRIPTION;
            $rows[] = $data;
        }
        $this->database->getHotels()->insert($rows);
    }

Problém je, že ani CLI nesnese přes tisíc záznamů a hodí mi SIGKILL. Jak to rozdělit, aby to přidávalo např. po 500 souborech?

Napadlo mě udělat podmínku a unsetnout pole, jenže to unsetne všechny záznamy. Jak na to? Díky.
tttt
Profil *
Teď to načteš celé do paměti a importuješ naráz, importuj to soubor po souboru:
 public function addParsedRecords(array $files): void
    {
        for ($i = 0; $i < count($files); $i++)
        {
            $record = $this->XMLParser->parse($files[$i]);
             $this->addRecordToDB($record);
        }
    }

Funkci $this->addRecordsToDB($records); budeš muset přepsat tak, aby importovala jeden záznam. Jestli to bude pořád pomalé, tak vygeneruj CSV a naimportuj ho rovnou do databáze, většinou pro to bývá podpora.
ForestCZE
Profil
tttt:
Díky za tip. Importovat to soubor po souboru asi není úplně šťastné, když to pošle přes tisíc INSERT INTO příkazů. Na to CSV bych mrknul, to zní lépe.
Keeehi
Profil
ForestCZE:
Jedním insertem můžeš vložit i více řádků.
ForestCZE
Profil
Keeehi:
No vždyť ano, tak to teď mám. Ale je potřeba to asi udělat jinak, když (nejspíš) přeteče paměť a já nemám tušení jak. Už dny se nemohu hnout z místa.
Keeehi
Profil
ForestCZE:
Na druhou stranu můžeš zavolat více insertů. Můžeš třeba číst ten xml soubor, generovat ten insert a když budeš mít třeba 100 záznamů, tak ten insert poslat do databáze. A pak začít s novým insertem pro záznam 101.
Jestli to má být 100, 10000 nebo něco jiného je prostě potřeba vyzkoušet.
ForestCZE
Profil
Keeehi:
A mohl bys mi prosím ukázat nějaký příklad, jak generovat ten insert? Nenapadá mě, jak na to. Děkuji.


EDIT: tady je asi vidět, proč mi to killne

tttttt
Profil *
ForestCZE:
Díky za tip. Importovat to soubor po souboru asi není úplně šťastné, když to pošle přes tisíc INSERT INTO příkazů.
A je to problém? Pokud je ta databáze na stejném serveru, tak tipuju, že úzké hrdlo je parsování XML. Změř si to, ať víš, jestli je to v tvém případě pravda. Myslím si, že to bude "good enough".
Pokud to chceš mít čisté, tak to importuj přes CSV, hromadný insert je takové polovičaté řešení. Nevidím důvod, proč ho použít, pokud je import přes CSV možný.
ForestCZE
Profil
tttttt:
Tak jsem překvapen, ale je to v pohodě. Dělám to teď tak, že mi to v cyklu parsuje soubor po souboru a hned ho to přidá do databáze. Nedává se to tam všechno najednou a RAMka je spokojená. Sežere okolo 300M :))

Vaše odpověď

Mohlo by se hodit


Prosím používejte diakritiku a interpunkci.

Ochrana proti spamu. Napište prosím číslo dvě-sta čtyřicet-sedm:

0