Chybné cachování Nette/Database způsobí zbytečné dotazy do databáze
- Eda
- Backer | 220
Zdravím.
Všiml jsem si, že na jedné stránce s komplikovanějším výpisem dat (výpis evidence vozů), kde využívám Nette\Database, se mi pokládá na databázi neuvěřitelné množství dotazů (kolem tisíce dotazů při výpisu 100 vozů). Začal jsem tedy pátrat a separovat problém. Ze stavu, kdy jsem měl 20 tabulek a různé průchody přes ref() a related() mezi nimi, jsem se dostal až do situace, kdy mám tabulky tři (car, operator a number; car má více dopravců, dopravce má více numberů) a v presenteru následující kód:
$cars = $this->db->table('car')->limit(100);
foreach ($cars as $car) {
foreach ($car->related('operator') as $operator) {
foreach ($operator->related('number') as $number) {
$number->number;
}
}
}
Čekal bych, že se položí tři dotazy (nepočítám-li první načtení po vymazání cache). Položí se jich ale pět. A to konkrétně tyto:
SELECT `id` FROM `car` LIMIT 100
SELECT `id`, `idvozu` FROM `operator` WHERE (`operator`.`idvozu` IN (...))
SELECT `id`, `idvuzdopravce` FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))
SELECT * FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))
SELECT `id`, `idvuzdopravce`, `number` FROM `number` WHERE (`number`.`idvuzdopravce` IN (...))
Tyto dotazy se položí vždy, nepočítám-li první načtení po vymazání cache, kdy se tahají informace o struktuře databáze.
V tabulkách car a number je asi 20 tisíc záznamů, v operator
40 tisíc. Když z libovolné z těchto tabulek vymažu většinu záznamů,
začnou se obratem pokládat jen 3 dotazy do databáze (tzn. problém nastává
jenom při velkém množství záznamů). Tabulky operator a number jsem osekal
o přebytečné sloupce a nechal tam jen cizí a primární klíče. Když se
pokusím osekat takto i tabulku cars, problém se opět vyřeší a začnou se
pokládat jen 3 dotazy. Problém je vyřešen, také když odstraním z kódu
$number->number;
a místo toho tam dám třeba jen
$number->id;
(= když potřebuji jen kolonky primárního nebo
cizího klíče, problém je vyřešen).
Docela zajímavé, že? :-D
Zkoušel jsem Nette krokovat s xdebugem, ale moc moudrý jsem z toho nebyl :-) Možná je problém někde u poznamenávání do $this->previousAccessedColumns a následnému ukládání do cache, ale nijak extra do toho nevidím.
Jen pro úplnost: Nette 2.0.10, PHP 5.4.6–1ubuntu1.1.
- hrach
- Člen | 1838
no, chlapce, dals mi zabrat. https://github.com/…base-caching
jeste k tomu teda ale musim napsat test… :)
- doba ramcoveho pochopeni problemu: 10min
- doba kompletniho pochopeni problemu: 1,5h
- doba na vyreseni: 30min
- doba na test: odhad 30min