De la 35 de secunde la 0,19: ce se întâmplă când o tabelă nu are indecși
Un dashboard care se încărca în 35 de secunde nu avea nevoie de server mai mare. Avea nevoie de trei indecși.
Aveam un dashboard care se încărca în 35 de secunde. Nu la vârf de trafic, nu sub atac — în condiții normale, cu un singur utilizator. Prima reacție a oricui, inclusiv a noastră pentru câteva minute, e „ne trebuie un server mai mare".
Nu ne trebuia. Ne trebuiau trei indecși. După ce i-am adăugat, aceeași pagină s-a încărcat în 0,19 secunde. Mai jos e cum am ajuns acolo, ce am măsurat și de ce diagnosticul greșit e mai scump decât problema.
Simptomul induce în eroare
O pagină lentă arată la fel indiferent de cauză. Poate fi PHP-ul, poate fi rețeaua, poate fi frontend-ul care descarcă prea mult, poate fi baza de date. Vizitatorul vede același lucru: o rotiță care se învârte.
De aici vine capcana. „Site-ul e lent" duce aproape reflex la „mai mult RAM", fiindcă e singura soluție care se cumpără cu cardul în cinci minute. Uneori chiar ajută — mascând problema până când datele cresc destul cât să reapară, de obicei mai rău.
Măsoară înainte să schimbi ceva
Regula pe care o aplicăm: nu atingem nimic până nu știm unde se duce timpul. Pe proiectele Laravel folosim Laravel Pulse, care arată interogările lente exact așa cum se execută în producție, nu cum arată în teorie.
În cazul nostru, din cele 35 de secunde, aproape tot timpul stătea într-un singur loc: interogările către tabela de articole. Restul — PHP, randare, rețea — însuma sub o secundă. Deci nu aveam o problemă de server. Aveam o problemă de citire a datelor.
Ce face de fapt un index
Fără index, baza de date caută un rând așa cum ai căuta un cuvânt într-o carte fără cuprins: pagină cu pagină, de la început. Se numește full table scan. Cu o mie de rânduri nu observi. Cu câteva sute de mii, observi foarte tare.
Un index e cuprinsul. Costă spațiu pe disc și încetinește puțin scrierile, fiindcă trebuie actualizat la fiecare rând nou. În schimb, transformă o căutare care citește tot tabelul într-una care sare direct unde trebuie.
De aceea nu se pun indecși pe orice coloană „ca să fie". Se pun pe coloanele după care chiar filtrezi, sortezi sau faci join — adică pe cele care apar în WHERE, ORDER BY și în legăturile dintre tabele.
Cum afli exact ce lipsește
Nu ghicești. Pui EXPLAIN în fața interogării lente și citești ce zice baza de date că are de gând să facă:
EXPLAIN SELECT * FROM articles WHERE source_id = 12 ORDER BY published_at DESC LIMIT 20;
Dacă în coloana type scrie ALL, ai un full table scan. Dacă în rows vezi un număr apropiat de totalul tabelei, citește tot ca să-ți dea douăzeci de rânduri. Ambele sunt semnale că lipsește un index pe coloana din WHERE sau din ORDER BY.
În Laravel, indexul se adaugă printr-o migrare, deci rămâne în istoricul proiectului și se aplică identic pe orice mediu:
$table->index(['source_id', 'published_at']);
Ordinea coloanelor contează
Un index pe două coloane nu e același lucru citit invers. Regula practică: coloana după care filtrezi stă prima, cea după care sortezi a doua. Un index pe (source_id, published_at) ajută o interogare care filtrează pe sursă și sortează după dată. Același index scris invers ajută mult mai puțin aceeași interogare.
E genul de detaliu care nu se vede în cod și nu apare la code review, dar face diferența între 35 de secunde și o fracțiune de secundă.
Rezultatul, măsurat
După adăugarea indecșilor pe coloanele care apăreau efectiv în interogări, dashboard-ul a coborât de la 35 de secunde la 0,19 secunde. Aceeași aplicație, același server, aceleași date. Singura schimbare: baza de date nu mai citea tot tabelul ca să afișeze un ecran.
Nu am schimbat planul de găzduire, nu am adăugat cache peste problemă și nu am rescris aplicația. Cache-ul pus peste o interogare proastă ascunde simptomul până la prima invalidare, apoi îl aduce înapoi — de obicei exact când ai trafic.
Ce poți verifica la tine
Dacă ai o pagină care se încarcă vizibil greu, în ordinea asta:
1. Măsoară unde se duce timpul, nu presupune. În Laravel, Pulse sau Telescope; în general, log-ul de interogări lente al bazei de date.
2. Ia cea mai lentă interogare și pune EXPLAIN în fața ei.
3. Dacă vezi ALL și un număr mare la rows, uită-te ce coloane sunt în WHERE și ORDER BY.
4. Adaugă indexul printr-o migrare, nu manual în phpMyAdmin — altfel dispare la următoarea instalare.
5. Măsoară din nou. Dacă nu s-a schimbat nimic, indexul nu era problema; nu-l lăsa acolo degeaba.
De ce contează dincolo de viteză
O pagină care se încarcă greu costă în două locuri deodată: vizitatorul care pleacă și poziția în Google, care ia în calcul viteza de încărcare prin Core Web Vitals. Diferența dintre 35 de secunde și 0,2 secunde nu e o chestiune de confort — la 35 de secunde nu mai are cine să aștepte.
Cazul complet, cu ce am construit în jurul lui, e pe pagina proiectului OSINT Romania. Dacă ai o aplicație care a început să se miște greu pe măsură ce i-au crescut datele, despre asta vorbim la aplicații web custom.
Un site lent nu înseamnă automat un server prea mic. De cele mai multe ori înseamnă o interogare care citește mai mult decât are nevoie. Măsoară întâi, apoi cumpără fier — de obicei nu mai e nevoie.