Problém N+1 dotazů
Také známo jako N+1, N+1 queries, Dotaz na každý řádek
Stručně
Jeden dotaz na seznam plus jeden na každý řádek: rychlé s 10 řádky ve vývoji, smrtelné s 500 v produkci.
Načtete seznam objednávek jedním dotazem a pak u každé objednávky zvlášť ptáte na zákazníka — a stránka najednou dělá 501 dotazů. Každý je rychlý. Dohromady jsou pomalé, protože čas tvoří cesta tam a zpět, ne data. Je to nejčastější důvod, proč se stránka chová jinak ve vývoji a v produkci.
Matematika, kterou ve vývoji nikdo nepočítá
S 10 řádky a 1 ms na dotaz je stránka hotová za 10 ms — neviditelně. S 500 řádky a 4 ms latence k databázi je to 2 sekundy ještě před vykreslením, a na sdíleném poolu spojení se ty požadavky řadí a zpomalí celý web. Zákazník číslo 501 v testovací databázi nemá tři předchozí objednávky, zákazník číslo 1 v produkci ano.
Projeví se to i jako útes latence: čas odpovědi roste lineárně s délkou seznamu. Stránka, která je v pořádku pro malé zákazníky, padne na tom největším — tedy na tom, u kterého si selhání nemůžete dovolit.
Jak to opravit
Opravte to tak, že vše, co vykreslujete, přijdete v jedné cestě. V SQL je to JOIN na druhou tabulku. V ORM je to eager loading (include, with, select_related) místo dotyku líného vztahu ve smyčce: jeden dotaz na seznam, jeden na všechna související data a spárování v paměti.
A pak to udržte testem: v CI ověřte počet dotazů pro těžkou stránku (například ≤5). Co nemá smysl: načítat napřed všechno „pro jistotu“ promění N+1 v over-fetching, který má taky svou cenu.
Související pojmy
PostgreSQL
Open-source relační databáze, se kterou je nejlepší začít: relace, joiny, transakce a JSONB v jednom prověřeném systému.
Cachování
Uložení drahého výsledku, aby další požadavek stál skoro nic — silné a snadno nenápadně špatné.
SQL vs NoSQL
Relační databáze jsou rozumná výchozí volba; dokumentová úložiště vyhrají jen tam, kde data opravdu nemají pevný tvar.