Selection – Při iteraci dochází paměť

ondrej256
Člen | 186
+
0
-

Zdravím,

narazil jsem na problém s pamětí v nette. Používám nette/database v2.4.11. PHP memory_limit mám 1024MB

Pokud si s databáze vytáhnu knihy, kterých je 112 881 a iteruju nad nima + nad každou knihou uvnitř ještě iteruji nad tagy, tak mě dojde paměť a vyskočí chyba:

Fatal Error: Allowed Memory Size of x Bytes Exhausted

$bookSelection = $this->bookRepository->findAll(); // 112 881 záznamů

foreach ($bookSelection as $book) {
    $items = [];

    $tags = $book->related('tag');

    $filteredTags = [];
    foreach ($tags as $tag) { // kniha větsinou obsahuje 2-4 tagy
        if (...) { // nejaka podminka, ktera neni dulezita
			$filteredTags[] = $tags;
		}
    }

   $this->processAuthors($authors); // funkce vyzaduje array, nesmi to byt selection
}

Ani jedna tabulka (authors ani tag) neobsahuje velký počet sloupců nebo nějaké drasticky velké data.
Je možné, že si nette database vytváří nějaké interní struktury/objekty/cokoliv co má vysoké nároky na paměť?

Nebo je to tím že je to prostě velké množství dat, které samo o sobě zabere celou paměť?

Předem díky za odpověď

Editoval ondrej256 (17. 3. 2021 12:18)

Pavel Kravčík
Člen | 1180
+
-1
-

Dotazy ve loopech je většinou cesta do pekel. Pokud už děláš nějaký složitější dotaz, ideálně ho udělej v jednom SQL. Pokud Ti jde čistě o seznam tagů ke knize – podívej se na GROUP BY.

David Grudl
Nette Core | 8111
+
+1
-

Pavel Kravčík napsal(a):

Dotazy ve loopech je většinou cesta do pekel. Pokud už děláš nějaký složitější dotaz, ideálně ho udělej v jednom SQL. Pokud Ti jde čistě o seznam tagů ke knize – podívej se na GROUP BY.

To není pravda, právě tohle NDBE řeší. Ale proč dochází paměť netuším.

Pavel Kravčík
Člen | 1180
+
0
-

Předpokládám, že vně toho kódu budou nějaké nové dotazy, co nejsou vidět v příkladu a ty to shazují.

OT: btw nejdeš komentovat z nějakého důvodu, jen nahlásit.

Šaman
Člen | 2634
+
0
-

@PavelKravčík OT: koukám, že to tak mají všechny příspěvky, které už obsahují citaci jiného příspěvku.

David Grudl
Nette Core | 8111
+
+2
-

Opraveno

ondrej256
Člen | 186
+
0
-

Sice jsem ten kód zjednodušil, ale žádné další skryté dotazy, které by mohly dělat problémy tam neprobíhají. Nicméně jsem udělal zajímavý objev.

Pro upřesnění kód spouštím přes konzoli (kdyby/console)

1. Pokus

  • Kód úspěšně proběhne
$bookSelection = $this->bookRepository->findAll(); // 112 881 záznamů

foreach ($bookSelection as $book) {
    $items = [];

    $tags = $this->tagRepository->findByBookId($book['id']);
//    $tags = $book->related('tag'); // RELATED SE NYNI NEPOUZIVA, taham primo pres tagRepository, tzn. bez JOINŮ

    $filteredTags = [];
    foreach ($tags as $tag) { // kniha větsinou obsahuje 2-4 tagy
        if (...) { // nejaka podminka, ktera neni dulezita
			$filteredTags[] = $tags;
		}
    }

   $this->processAuthors($authors); // funkce vyzaduje array, nesmi to byt selection
}

2. Když smažu cache

$bookSelection = $this->bookRepository->findAll(); // 112 881 záznamů - spadne hned na prvním řádku na paměti
foreach ($bookSelection as $book) {
    $items = [];

    $tags = $book->related('tag');
 $filteredTags = [];

   // PRVNI ITERACE PROBEHNE, ALE DALSI ITERACE UZ NEPROBEHNE, DOJDE PAMET

    foreach ($tags as $tag) {
	// TADY UZ NEDOJDE ANI K PRVNI ITERACI
 ...
}
 ...
}

3. Když smažu cache a spustím

$bookSelection = $this->bookRepository->findAll()->limit(1); // OMEZIM POUZE NA JEDEN ZAZNAM

foreach ($bookSelection as $book) {
    $items = [];

    $tags = $book->related('tag'); // FUNGUJE I S POUZITIM RELATED
    $filteredTags = [];
    foreach ($tags as $tag) { // kniha větsinou obsahuje 2-4 tagy
        if (...) {  // nejaka podminka, ktera neni dulezita
			$filteredTags[] = $tags;
		}
    }

   $this->processAuthors($authors); // funkce vyzaduje array, nesmi to byt selection
}

tak nyní skript v pořádku s jedním záznamem doběhne. A pokud v tuto chvíli vyhodím ->limit(1) a nechám skript běžet v původním stavu, tak najednou doběhne v pořádku. Mám z toho dojem, že je to nějaký problém s cachováním. Jakoby to po smazání cache dotahovalo hrozně moc informací, které zaberou spoustu paměti, ale při druhém pokusu už jsou ty informace v cachi a vše proběhne bez problémů.

Polki
Člen | 553
+
+2
-

Nette si namapuje, jaké dotazy nad čím děláš. Pokud děláš dotazy v cyklu, tak při prvním průchodu si nette mapuje, co taháš, ale aby to fungovalo musí dotazy udělat zvlášť. To znamená více dotazů, větší traffic a taky větší zátěž.

Jakmile ale jednou doběhnou dotazy v pohodě (například když dáš limit(1)), tak Nette zjistí, jakým způsobem to taháš a vytvoří si nejefektivnější možné dotazy a ty uloží pro ten který případ do cache.

V dalším volání už to bude strukturu dotazu tahat z cache, což zapříčiní to, že se provedou dotazy jen 2 Jeden na tabulku book a druhý na tabulku tag, čímž se ten traffic neuvěřitelně zmenší. Proto ti to před vytvořením cache padá a po vytvoření cache ne, ale po smazání cache zase jo.