Autor Zpráva
Peet
Profil
Zdravim,

chtel bych si udelat trochu jasno obecne v pameti a procesech.

Mam totiz problem pri importu objemnych xml feedu. Muj stavajici pripad je takovy, ze spoustim cronem pres php cli jeden script, ktery postupne nacita xml feedy (celkem je jich asi 270) Pri prvnim testu, mi padnul server kvuli pameti, tak jsem dokoupil vetsi pamet. Ted uz to jede v poradku, ALE

1) kdyz se import spusti, tak se kouknu na stav --top-- a tam vidim ze proces php zabira 100% cpu, co to presne zmanema ? ze je cpu maximalne vytizeny a nic jineho nezvladne ? Pokud to tak neni, tak jak zjistim realnu stav procesoru ?

2) co se stane kdyz bych rozdelil feedy treba po 50ti a spustil je cronem soubezne ? Znamena to, ze kdyz mi jeden cron job zere cca 1.5gb ram, a ja bych jich spustil 10 soubezne, tak potrebuju min 15gb ram ? a co tech 100% cpu ?

3) Kdyz je u php fpm nastavena memory_limit na 512 na jeden proces, tak chapu spravne, ze napriklad nginx nebo apache, pracuje stale s jednim procesem ? tudiz kdyz prijde 100 lidi na stranku, tak vsichni vyuzivaji ten jeden proces ?

4) Mam v databazi nekolik desitek tisic dat, v jedne tabulce napriklad 60 000 a kdyz si udelam obycejny grid a dam select all pres doctrinu, a pokusim se zobrazit stranku, tak za prve to padne na memory_limit a kdyz ji navysim na 1gb ciste pro testovaci ucely, tak se mi zobrazi tabulka, ale je nacetla treba jen do pulky a zbytek stranky chybi, pravdepodobne to bude jeste nejaky jiny limit, ktery ukonci proces tahani dat

5) Kdyz pouzivam doctrine, mam se koukat v php na nastaveni mysql, mysqli, pdo ? na newrelicu vidim, ze doctrine jede na sqlite.. jake jsou tu tedy rozdily a kde bych mel spravne hledat konfigurace ?


Budu opravdu rad za jakoukoliv jednotlivou odpoved. Nepovazuji se za zadneho experta pres servery, proto bych se zase rad neco priucil.
Diky
TomášK.
Profil *
Zdá se mi, že to parsování je napsané strašně neefektivně. Pro potřebu GB paměti bych čekal, že ty feedy budou mít víc než 100 MB a v takovém případě bych zvážil přepsat to tak, aby to nenačítalo celý dokument do paměti.

1) 100 % na CPU znamená, že daný proces od poslední aktualizace využíval veškerý dostupný čas jednoho jádra procesoru. Pokud přijde další proces, který chce používat procesor, rozdělí operační systém čas procesoru mezi procesy. Systém, podle kterého to dělá, je poměrně složitý a většinou funguje docela dobře. Nastavením priorit se dá ovlivnit, který proces kolik dostane.

2) Víceměně ano. Víceměně proto, že paměť přidělená procesu není jedno číslo. CPU rozdělí operační systém mezi procesy, nejefektivnější bývá počet procesů nastavit na +- počet vláken procesoru (-1, chci-li volné vlákno, dvojnásobek, chci-li zvýšit šanci, že vždy bude co počítat). Při více instancích procesu se začne spotřebovávat zbytečně mhoho času na přepínání procesů.

3) Ne, znamená to, že jeden proces má limit 512. Počet procesů se nastavuje jiným parametrem (z hlavy si ho nepamatuju, nemělo by být těžké ho dohledat).

4) Tohle se myslím dělo, když nestačil v nginxu nějaký buffer. V jeho error logu by mohlo být napsané, že něco zaříznul. Nicméně posílat do prohlížeč 60 000 řádků není dobrý nápad.
Peet
Profil
Diky za vysvetleni, popisu postup importu

v prvnim kroku mam

$xml = @simplexml_load_file($feedEntity->getUrl());
        if (!$xml) {
            Debugger::log('feed_id: ' . $feedEntity->getId() . ' is not valid XML | url: ' . $feedEntity->getUrl());
            return;
        }
        $xml = new \SimpleXMLElement($feedEntity->getUrl(), null, true);

Ted uz presne nevim, ale tusim, ze SimpleXmlElement nevyhazoval Exception pri spatne url, tudiz jsem nemohl odchytit chybnou url, proto jsem dal jeste pred to nacteni pres simplexml_load_file (ano ted mi dochazi, ze uz jen tohle mi asi nacte soubor 2x do pameti)

A zda se mi, ze po spusteni cronu, to nacita vcelku rychle, ale asi tak po 10ti hodinach, uz to nacita strasne pomalu, tudiz to musi delat neco co se v ramci toho jednoho procesu stale udrzuje a asi i zvetsuje, napada me tedy, jestli nahodou SimpleXmlElement nemusim na konci nejak zahodit, uzavrit, jestli mi nahodou nezustavaji pak veskere predchozi nactene feedy v pameti a proto se to vice a vice zpomaluje.


No v dalsim kroku prochazim jednotlive atributy, dotazuji se na existenci zaznamu, vytvarim nove a ve vysledku persistnu a po precteni celeho feedu, hodim flush.

1) Aha, a mohu nekde tedy zjistit realny stav procesoru ? Tzn. abych to mohl videt jak mam vytizeny procesor

2) Kde mohu nastavovat pocet procesu ? a jak mam vedet chci-li volne vlakno a nebo radeji dvojnasobek :D ? (ja vim, pro tebe asi blba otazka, ale snazim se to nejak pochopit)

3) Jasne je to tedy memory_limit na jeden proces, slo mi spis o to pochopit, jak se to chova kdyz na web prijde nekolik tisic lidi, pravdepodobne tedy nginx drzi jeden proces fpmka, a pokud je nejak hodne vytizenej, tak spusti dalsi proces,a maximalni pocet procesu se tedy asi nastavi nekde v nginx configu, nebo to chapu stale spatne ?

4) Jo tak jasne, ze nebudu nikdy tolik zaznamu vypisovat najednou, ted je to jen v adminu, kde jsem pouzil nejakej Grido, do ktereho se musi nasypat vsechna data, musim to predelat, nicmene jsem chtel vedet zda to killne nejaky render a nebo to killne mysql dotaz, protoze tam byl jeden sql, ktery mel asi 3 joiny, nic sloziteho, na frontendu budu muset pocitat mnohem slozitejsi dotazy, proto bych chtel vedet co za to muze. v logach jsem prave nic nenasel..

Diky za ochotu :)


ad 2) Myslis, ze je lepsi, efektivnejsi poustet jeden job a nebo nastavit vice jobu a poustet je najednou ? Pokud poustet v jednom jobu, tak proc ? (kdyz nepocitame RAM)
TomášK.
Profil *
A zda se mi, ze po spusteni cronu, to nacita vcelku rychle, ale asi tak po 10ti hodinach, uz to nacita strasne pomalu, tudiz to musi delat neco co se v ramci toho jednoho procesu stale udrzuje a asi i zvetsuje

Cron jen spustí proces, neudržuje žádný stav/paměť mezi jednotlivými instancemi. Pokud ty procesy samotné neběží 10 hodin, není rozdíl mezi procesem spuštěným poprvé a podesáté. Nanejvýš se někde můžou hromadit dočasné soubory, něco v databázi.

1) Nahoře v `top` je, kolik procent času z dostupného CPU spotřeboval, pro každé jádro. Je-li to VPS, možná to není úplně důvěryhodné.

2) Myšleno procesů, které spouštíš. Spustíš-li import 10krát, budeš jich mít 10. Ideální počet procesů si změř - spusť import s různými počty a vezmi to, co je nejrychlejší.

3) Nginx je "hloupý" (narozdíl od apache), jen přesměrovává požadavky na port/socket, kde běží php-fpm. Na serveru běží php-fpm deamon, který spouští php-procesy, konfigurace jejich počtu a spawnování bude v konfiguraci php-fpm. Samotný nginx má ale taky své procesy a limit na počet příchozích spojení, většinou větší než php.
Peet
Profil
Rozumim.

Jo, vim ze cron jen spusti skript, v mem pripade php. v tom php skriptu mam foreach prave tech feedu a postupne spouteni importu. Tak me napadlo, jestli to nemuze delat treba ten simpleXMLElement, nejprve me napado, ze by mohl byt problem v tom, ze pouzivam flush az na konci importu kazdeho feedu, tudiz se drzi prilis mnoho entit v EntityManager a proto je to php pak pomale, ale kdyz jsem dal flush po kazdem radku, bylo to pomalejsi, a zjistil jsem, ze pravdepodobne se ten fush provadi nahodne, ze to ten entityManager nejak hlida, a sam flushuje.

Zkusim trochu zoptimalizovat nacitani pomoci XmlReaderu a zkusim spustit napr. 4 instance php importu a uvidim jak to bude rychle.
Keeehi
Profil
Peet:Myslím si, že to co máš v [#3] v paměti ten soubor nedrží dvakrát. Když na řádku 6 přepíšeš obsah proměnné $xml, měl by se z paměti uvolnit i její obsah.

ad 2) Myslis, ze je lepsi, efektivnejsi poustet jeden job a nebo nastavit vice jobu a poustet je najednou ? Pokud poustet v jednom jobu, tak proc?
Lepší je více menších jobů než jeden velký. Když selže jeden z těch menších, mohou se ostatní stále dokončit. Když to bude vše v jednom, tak se po selhání ty zbývající nedokončí.

Když už jsme u jobů, tak bych ti doporučil sáhnout po nějakém notovém řešení správy fronty jobů. Typicky to funguje na modelu producent - consumer. Když chceš udělat nějakou práci, vytvoříš job a úložišíš ho do fronty. Tam čeká, až se na něj dostane řada. To je ta producent část. Úplně nezávisle na tom běží proces, který si z fronty postupně bere joby a spouští je. Jobům se typicky dá nastavovat priorita, takže pokud přijde nějaký s vysokou prioritou, může ve frontě přeskočit ty s nižší a dostat se tak dříve ke zpracování. Dá se nastavit že pokud job selže, tak kolik má být ještě pokusů o jeho znovuspuštění než bude označen za nedokončený. Také se dá nastavit prodleva mezi takovými pokusy.
Co se týče fronty zpracovávající joby, tak jich může být více. Tím se zajistí paralelní zpracování. Některé fronty mohou zpracovávat všechno, některé budou určené třeba jen pro joby s vysokou prioritou.
V neposlední řadě také umožňují zjišťovat kdy se joby spustily, jak dlouho běžely, proč případně selhaly, jaké bylo jejich nastavení atp.


Před spoustou let jsem napsal třídu, která uměla při použití malého množství paměti rozdělit velký XML soubor do několika menších. Buď se dá použít k tomu k čemu byl vytvořen a importovat postupně více menších souborů, nebo je možné ho využít jako základ pro vytvoření vlastního XML parseru, který bude velký soubor načítat postupně a nebude ho mít tedy celý načtený v paměti. Je taky dost možné, že něco takového už někdo vytvořil a existuje na to veřejně dostupná knihovna. Takže bych se po něčem takovém podíval.
Peet
Profil
Keeehi:
Mockrat diky za zpravu, cely den na tom pracuju a zkousim ruzna reseni a testuju cas, pamet, procesor atd. A uz to zacina importovat opravdu rychle, udelal jsem nasledujici zmeny:

1) nejprve jsem cele xml nacetl do SimpleXmlElement() takze to zabralo hodne pameti a pravdepodobne to bylo i casove narocnejsi pro php, nyni jsem sahnul po XmlReaderu, ktery pouze parsuje xml, takze dejme tomu, kdyz nacitam nejake produkty, kde jich je 1000 v xml, tak pres XmlReader prochazim elementy <product> a teprve az z jeho obsahu vytvarim novou instanci SimpleXmlElement(), takze je vzdy vytvorena jedna instance na jeden produkt.

2) zjistil jsem, ze na nejakych mistech jsem volal flush() tudiz se zmeny promitly do databaze a takhle vzdy treba po 3 radcich, omezil jsem flushovani pouze na veci, ktere v tu chvily nutne potrebuji promitnout do db (napriklad nejake typy produktu u kterych pote potrebuji v dalsim cyklu overit jejich existenci a pripadne vratit pouze ID) takze se tim padem zvetsily transakce a nyni se promitaji zmeny cca po 800 radcich a skript je minimalne 2-3x rychlejsi

V posledni rade bych rad zjistil proc se skript postupem casu behu zpomaluje. Nevim zda si opravdu neco uklada v prubehu importu jednotlivych feedu nebo kde je problem, ale jakmile bezi jeden job vice jak hodinu, tak pak zpomaluje.. ale zaboha nemohu prijit na to proc.

Napadlo me tedy rozdelit feedy na nekolik jobu, ale zde zase vznika problem, ze potrebuji volat jeden job az uplne jako posledni, proto jsem to dal do jednoho jobu a ten skript dal az na konec. Ty fronty by to mohly vyresit, mas nejaky tip co na to pouzit ? :)
Keeehi
Profil
Pokud chceš vědět, jak se chová paměť, tak si postupně někam zaznamenávej php.net/manual/en/function.memory-get-usage.php a uvidíš, zda ti stále roste.

Na fronty jsem používal laravel.com/docs/5.6/queues ale to souvisí s tím, že jsem používal Laravel jako framework.
Peet
Profil
Keeehi:
To bude to same, jako kdyz sleduji stav v --top-- ne ? to totiz vypada, ze se moc nemeni, mozna trochu, ale neznatelne.

jj uz jsem na neco koukal, ted premyslim zda to nebude moc komplikovane, zkratka co se tyce toho, ze by jeden import selhal, tak to je zcela v poradku, mam to dost osetrene a hlavne dost logovane. Spis me zajima jak to funguje, nasel jsem nejake addons pro Nette na corn tasks, ale pokud to chapu spravne, takhlavni cron job, spusti opet nejakou php sluzbu, ktera potom ridi ty skripty, ve finale se o to ale stara stale jedna instance php nebo se pletu ?

Edit:
Tak beru zpet, opravdu si uklada neco do pameti, protoze potom co dojede jeden feed, ktery ma asi 80 000 zaznamu je usage ram asi 2,4gb a jakmile zacne nacitat dalsi feed, tak obsazenost zustava stejna, tedy nekde neco uklada.. jak mohu zjistit co ?

Vaše odpověď


Prosím používejte diakritiku a interpunkci.

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