Prečo je web aplikácia pomalá: 8 príčin na serveri a ako ich opraviť
Na začiatku bola aplikácia bleskurýchla. O rok neskôr sa prehľad objednávok načítava desať sekúnd, export do Excelu padá a používatelia klikajú dvakrát, lebo si myslia, že sa nič nedeje. Takmer nikdy za tým nie je slabý server, ale zopár typických chýb v tom, ako aplikácia pracuje s databázou a dátami. Dobrá správa: dajú sa nájsť meraním a väčšinou opraviť za pár hodín.
Čo si z článku odnesiete
- Pomalosť merajte, nehádajte — profiler a log pomalých dotazov ukážu presné miesto.
- Najčastejší vinníci sú N+1 dotazy a chýbajúce indexy v databáze.
- Pomalé úlohy (e-maily, PDF, exporty) patria do fronty na pozadí, nie do požiadavky.
- Cache pomôže, ale až keď sú dotazy v poriadku — nie namiesto opravy.
Najprv merať, potom opravovať
Najdrahšia optimalizácia je tá, ktorá rieši nesprávny problém. Týždeň prepisovania frontendu nepomôže, ak aplikácia stojí na jednom databázovom dotaze, ktorý trvá štyri sekundy. Preto prvý krok nie je oprava, ale meranie.
- Profiler vo frameworku — ukáže, koľko dotazov stránka spustila a ako dlho trvali (napríklad Laravel Debugbar či Telescope, Django Debug Toolbar).
- Monitoring výkonu (APM) — v produkcii zbiera časy všetkých požiadaviek a ukáže najpomalšie a najčastejšie miesta.
- Log pomalých dotazov v databáze — zapíše každý dotaz, ktorý prekročí nastavený čas.
V MySQL či MariaDB zapnete log pomalých dotazov dvoma príkazmi — tento zachytí všetko, čo trvá dlhšie ako pol sekundy:
SET GLOBAL slow_query_log = 'ON';
SET GLOBAL long_query_time = 0.5;Nesledujte priemer, ale 95. percentil — čas, pod ktorým sa zmestí 95 % požiadaviek. Priemer 300 ms vyzerá skvele, aj keď každý dvadsiaty klik trvá päť sekúnd. A práve tie si ľudia pamätajú.
Osem príčin, ktoré nachádzam najčastejšie
1. N+1 dotazy
Zoznam 50 objednávok načíta objednávky jedným dotazom — a potom pre každú zvlášť zákazníka. Z jedného dotazu je zrazu 51 a so stránkou po 200 riadkoch 201. V kóde to vyzerá nevinne, pretože ORM načítava súvisiace záznamy „samo“, až keď ich prvýkrát použijete.
SELECT * FROM orders ORDER BY created_at DESC LIMIT 50;
SELECT * FROM customers WHERE id = 17;
SELECT * FROM customers WHERE id = 23;
...a ďalších 48 takmer rovnakých dotazovRiešením je načítať súvisiace záznamy naraz, jedným dotazom pre všetky. V Laraveli to je jedno slovo navyše — metóda with — a z 51 dotazov sú dva:
Order::with('customer')->latest()->paginate(50);
SELECT * FROM orders ORDER BY created_at DESC LIMIT 50;
SELECT * FROM customers WHERE id IN (17, 23, 31, 42);2. Chýbajúce indexy
Kým má tabuľka tisíc riadkov, databáza ju prejde celú za zlomok sekundy. Pri miliónoch riadkov to trvá sekundy — pri každom načítaní stránky. Index funguje ako register v knihe: databáza nemusí listovať všetkými stranami. Príkaz EXPLAIN ukáže, či ho dotaz používa:
EXPLAIN SELECT * FROM orders
WHERE customer_id = 42 AND status = 'paid'
ORDER BY created_at DESC;
CREATE INDEX idx_orders_customer_status_created
ON orders (customer_id, status, created_at);Pri zloženom indexe záleží na poradí stĺpcov: najprv tie, ktoré porovnávate na rovnosť, potom tie, podľa ktorých triedite alebo hľadáte rozsah. A s indexmi to neprežeňte — každý spomaľuje zápis.
3. Načítavanie všetkého
SELECT * načíta aj stĺpce, ktoré nepotrebujete, vrátane dlhých textov. A spočítať desaťtisíc riadkov v PHP či JavaScripte je vždy pomalšie než jedno COUNT alebo SUM priamo v databáze. Pri hlbokom stránkovaní navyše OFFSET 50 000 núti databázu prejsť 50 000 riadkov len preto, aby ich zahodila — rýchlejšie je stránkovanie podľa kurzora, teda „daj mi 50 záznamov za posledným, ktorý som videl“.
4. Pomalé úlohy priamo v požiadavke
Odoslanie e-mailu, vygenerovanie PDF faktúry, zmenšenie nahranej fotky či volanie externého systému — ak to všetko beží počas požiadavky, používateľ čaká na každú z týchto vecí. Presuňte ich do fronty: aplikácia úlohu len zaradí, hneď odpovie a pracovník na pozadí ju vybaví o sekundu neskôr. Pri dlhých exportoch pošlite hotový súbor e-mailom alebo notifikáciou.
5. Externé služby bez časového limitu
Platobná brána, kuriérska služba alebo účtovný systém občas odpovedajú tridsať sekúnd — a vaša stránka čaká s nimi. Každé volanie von musí mať časový limit (typicky 2 až 5 sekúnd), rozumný počet opakovaní s odstupom a pri výpadku náhradné správanie. Čo sa nemení každú sekundu, uložte do cache.
6. Chýbajúca cache
Štatistiky na nástenke, ktoré sa počítajú zo všetkých objednávok pri každom otvorení, pritom sa menia raz za hodinu. Takéto výsledky uložte do cache (napríklad v Redise) na minúty a obnovte ich pri zmene dát. Cache však nie je náplasť na zlý dotaz — ak je dotaz pomalý, prvý používateľ po vypršaní cache čaká rovnako dlho.
7. Zámky: požiadavky, ktoré na seba čakajú
Záhadný prípad: stránka spustí päť paralelných požiadaviek, ale vybavujú sa jedna po druhej. Častou príčinou je sedenie (session) uložené v súboroch — PHP ho počas požiadavky zamkne a ďalšie požiadavky toho istého používateľa čakajú. Riešenie je zatvoriť sedenie hneď, keď ho už nepotrebujete, alebo ho ukladať do Redisu či databázy. Podobne škodia dlhé databázové transakcie, ktoré zbytočne držia zamknuté riadky.
8. Obrovské odpovede
API vráti päť megabajtov JSON-u so všetkými vnorenými dátami, hoci obrazovka zobrazí desať riadkov. Posielajte len polia, ktoré klient potrebuje, stránkujte a zapnite kompresiu (gzip alebo Brotli). Tieto princípy podrobnejšie rozoberám v článku o návrhu REST API.
Rýchla diagnostika podľa príznakov
Kým spustíte profiler, príznak vám často napovie, kde hľadať:
| Príznak | Pravdepodobná príčina |
|---|---|
| Každá ďalšia strana zoznamu je pomalšia | Stránkovanie cez OFFSET, chýbajúci index |
| Počet dotazov rastie s počtom riadkov | N+1 dotazy |
| Pomalé len niekedy, náhodne | Externá služba, zámky, úloha plánovača |
| Pomalé len odoslanie formulára | E-mail, PDF alebo API volanie v požiadavke |
| Paralelné požiadavky čakajú na seba | Zamknuté sedenie alebo transakcia |
| Pomalé ráno, keď začnú všetci pracovať | Špička bez cache, slabé indexy |
| Časom stále pomalšie, bez zmeny kódu | Rastúce dáta bez indexov a stránkovania |
Kedy siahnuť po silnejšom serveri
Až na konci. Dvojnásobný výkon servera v najlepšom prípade zdvojnásobí rýchlosť. Správny index zrýchli dotaz aj stonásobne. Ak najprv kúpite silnejší server, problém sa len odloží — a dáta medzitým ďalej rastú.
N+1 dotazy a chýbajúce indexy rastú spolu s dátami. Čo dnes zamaskuje drahší server, o rok spomalí aj ten. Silnejší hardvér, repliky databázy či viac pracovníkov na frontu dávajú zmysel až po tom, čo je kód v poriadku.
Pomalá aplikácia takmer nikdy nepotrebuje nový server. Potrebuje niekoho, kto sa pozrie, čo robí s databázou. Zásada výkonu web aplikácií
Časté otázky
Ako zistím, ktorá časť aplikácie je najpomalšia?
Nasaďte profiler alebo nástroj na monitoring výkonu a zapnite log pomalých dotazov. Za pár dní uvidíte, ktoré stránky a dotazy trvajú najdlhšie a ako často sa volajú. Optimalizujte to, čo je pomalé a zároveň časté.
Pomôže prechod na inú databázu alebo framework?
Takmer nikdy to nie je prvý krok. Väčšina problémov s výkonom je v tom, ako aplikácia s databázou pracuje, nie v databáze samotnej. Prepis bez merania často prenesie tie isté chyby do nového systému.
Aká rýchlosť odozvy je dobrá?
Pre bežné obrazovky web aplikácie je dobrým cieľom odpoveď servera do 200 až 300 milisekúnd a pri 95 % požiadaviek do jednej sekundy. Nad dve sekundy ľudia začínajú klikať opakovane a strácať trpezlivosť.
Zhrnutie
Kľúčové poznatky
- Začnite meraním — profiler, monitoring a log pomalých dotazov; sledujte 95. percentil.
- N+1 dotazy odstráňte načítaním súvisiacich záznamov naraz.
- Pridajte indexy podľa výsledku EXPLAIN a stránkujte kurzorom, nie hlbokým OFFSETom.
- E-maily, PDF a exporty presuňte do fronty; externým službám dajte časový limit.
- Cache a silnejší server až po oprave dotazov — nie namiesto nej.
Spomaľuje vás vlastná aplikácia?
Robím audit výkonu aj vývoj web aplikácií na mieru — nájdem, kde aplikácia stráca čas, a opravím dotazy, indexy, cache aj fronty. Pozrite si, ako pristupujem k tvorbe web aplikácií, alebo mi napíšte.
Čítajte ďalej
Ďalšie články
Bezpečnosť web aplikácie: 10 chýb, ktoré útočníci milujú
SQL injection, XSS, IDOR aj slabé sedenia. Desať najčastejších dier podľa OWASP a presné kroky, ako každú z nich zavrieť.
Čítať článokAko navrhnúť REST API, ktoré prežije roky a vývojári ho pochvália
Verziovanie, stránkovanie, konzistentné chyby a idempotencia. Pravidlá, vďaka ktorým sa vaše API používa ľahko aj po piatich rokoch.
Čítať článokVytvorte si vlastný AI marketingový tím
AI nástroje, ktoré nahradia časť marketingového oddelenia. Ako si krok po kroku poskladať vlastný AI tím a ušetriť desiatky hodín.
Čítať článok