Publikováno
· Autor: Zdeněk Hejzlar · Pod kapotou · Výkon · 3 min čtení
Obsah článku
Pomalá aplikace odrazuje návštěvníky i Google. Dobrá zpráva je, že většina zpomalení v Laravelu má pár opakujících se příčin a nejde o žádnou černou magii. Zkušenost mě naučila, že za devět z deseti pomalých stránek můžou databázové dotazy, ne pomalé PHP. Výkonová rezerva navíc pomáhá, aby web nespadl ve špičce — o příčinách výpadků píšu v článku proč web spadl a jak tomu předejít. Projdu příčiny, které řeším nejčastěji, a ukážu je na kódu.
Zbavte se N+1 dotazů
Nejčastější výkonnostní problém vůbec. Aplikace v cyklu sáhne do databáze pro každý řádek zvlášť. Místo dvou dotazů jich najednou běží stovky.
// ŠPATNĚ: N+1 dotazů
$posts = Post::all();
foreach ($posts as $post) {
echo $post->author->name; // dotaz pro každý příspěvek
}
// SPRÁVNĚ: eager loading, jen dva dotazy
$posts = Post::with('author')->get();
Rozdíl je brutální: kde dřív běželo dvě stě dotazů, teď běží dva. Na malé tabulce si toho nikdo nevšimne, ale jakmile dat přibude, aplikace se začne plazit. Proto N+1 hlídám od začátku, ne až když si zákazník stěžuje.
Cachujte, co se nemění
Opakovaný výpočet nebo dotaz, jehož výsledek se skoro nemění, je zbytečná zátěž. Cache ho odloží stranou a příště jen vrátí hotový výsledek, aniž by se znovu ptala databáze. Rozdíl mezi dotazem na databázi a čtením z paměti je řádový — z desítek milisekund se stanou desetiny.
use Illuminate\Support\Facades\Cache;
// Výsledek se uloží na hodinu
$sluzby = Cache::remember('sluzby', 3600, function () {
return Service::orderBy('sort_order')->get();
});
- Cachujte číselníky, nastavení a náročné agregace.
- Na produkci cachujte konfiguraci i routy příkazem
artisan optimize. - Nezapomeňte cache při změně dat zneplatnit.
Právě zneplatnění je nejčastější zdroj chyb — zapomenete smazat cache po úpravě a zákazník vidí staré ceny. Proto cachuji hlavně data, která se mění zřídka a předvídatelně. U často měněných dat se cache spíš vyplatí navázat na konkrétní událost, která ji sama zruší.
Zrychlete databázi
I dobře napsaný dotaz je pomalý, když databáze musí projít celou tabulku. Tady pomáhají indexy nad sloupci, podle kterých filtrujete.
// Migrace: index nad často filtrovaným sloupcem
Schema::table('posts', function (Blueprint $table) {
$table->index('published_at');
$table->index(['category_id', 'is_published']);
});
- Indexujte cizí klíče a sloupce ve
whereaorder by. - Vybírejte jen potřebné sloupce místo
select *. - Dlouhé operace přesuňte do fronty, ať uživatel nečeká.
Měřte, než začnete ladit
Optimalizace naslepo je ztráta času. Nejdřív najděte úzké hrdlo, teprve pak ho řešte.
- Používejte Laravel Debugbar nebo Telescope pro přehled dotazů.
- Rychlost se promítá do Core Web Vitals a tím i do SEO.
- Souvislost s vyhledáváním rozebírám v technickém SEO checklistu.
A na závěr férově: neoptimalizujte, dokud nemusíte. Předčasná optimalizace umí kód zbytečně zamotat a přinést chyby tam, kde předtím nebyly. Dokud aplikace běží svižně a data se vejdou do rozumných mezí, nechte ji být. Ladit má smysl tehdy, když měření ukáže konkrétní úzké hrdlo. Máte aplikaci, která se pod zátěží zadýchává? Napište mi a najdeme, kde ztrácí čas.