Când o interogare SQL rulează greu în producție, simpla ghicire a cauzei (‘poate lipsește un index’) este o abordare neprofesionistă. Utilitarul EXPLAIN ANALYZE rulează efectiv interogarea și afișează planul de execuție ales de planificatorul de costuri PostgreSQL alături de timpii reali de rulare.
Adăugarea parametrului BUFFERS este esențială: aceasta dezvăluie exact câte blocuri de memorie au fost citite direct din memoria cache a bazei de date (Shared Hit Blocks) și câte au necesitat citiri lente de pe hard disk/SSD (Shared Read Blocks).
Raportul evidențiază instant diferența dintre estimările planificatorului (`rows=10`) și numărul real de rânduri returnate (`actual rows=500000`), semnalând necesitatea actualizării statisticilor prin comanda `ANALYZE`.
Sintaxă, opțiuni și concepte cheie
| Opțiune / Concept | Descriere practică |
|---|---|
ANALYZE |
Execută efectiv interogarea și măsoară timpul real per nod din arborele de execuție. |
BUFFERS |
Afișează utilizarea memoriei tampon: blocuri citite din cache (hit) vs blocuri citite de pe disc (read). |
Seq Scan (Sequential Scan) |
Avertisment critic ce indică scanarea întregului tabel linie cu linie din lipsa unui index. |
Index Scan vs Index Only Scan |
Index Only Scan este idealul de performanță: datele sunt extrase exclusiv din index fără citirea tabelei. |
Exemple practice și cod
Exemplul 1: Profilarea unei interogări lente pe un tabel mare de utilizatori
Cum se interpretează raportul de execuție.
$ EXPLAIN (ANALYZE, BUFFERS)
SELECT nume, email FROM utilizatori WHERE status = 'activ' AND creat_la >= '2026-01-01';
Seq Scan on utilizatori (cost=0.00..1845.00 rows=1250 width=45) (actual time=0.045..14.210 rows=1420 loops=1)
Filter: ((creat_la >= '2026-01-01'::date) AND (status = 'activ'::text))
Rows Removed by Filter: 85000
Buffers: shared hit=420 read=820
Planning Time: 0.180 ms
Execution Time: 14.850 ms
# Diagnostic: 'Rows Removed by Filter: 85000' arată că PostgreSQL a citit 85.000 de rânduri de pe disc inutil!
# Soluție: Crearea unui index compus: CREATE INDEX idx_utilizatori_status_data ON utilizatori(status, creat_la);
Rezultat & Impact
După adăugarea indexului, timpul de execuție scade de la 14.8ms la sub 0.2ms!
Greșeli frecvente și bune practici
- Rularea comenzii `EXPLAIN ANALYZE DELETE …` sau `UPDATE …` pe o bază de date de producție fără a realiza că interogarea este executată real (datele vor fi modificate sau șterse real; folosiți o tranzacție cu `ROLLBACK`).
- Ignorarea liniei `Rows Removed by Filter`, care indică clar lipsa unui index potrivit.