Aceasta este o copie de probă. Site-ul adevărat este data-analist.com.
contact@data-analist.com
str. Igor Vieru 15, Chișinău Republica Moldova

Automatizăm procese. Analizăm date. Găsim soluții.

Indexuri în baza de date: ce trebuie să știe un analist
HomeSQL & Databases Indexuri în baza de date: ce trebuie să știe un analist
Un query care durează 18 secunde poate să scadă la 0.4 cu un index potrivit. Cu unul nepotrivit, te trezești cu un INSERT de 12 ori mai lent. Aici e linia care contează.

„Query-ul rulează în 18 secunde, nu suportă șeful.” E cea mai frecventă plângere a unui data analyst care lucrează pe o bază de date crescută organic. În 80% din cazuri, vinovatul nu e SQL-ul prost scris. Sunt indexurile lipsă — sau, mai rar dar la fel de tăios, indexurile greșite. Subiectul indexuri SQL analist e teritoriu confuz pentru mulți analiști, pentru că pică între ce face un dezvoltator backend și ce face un DBA. În realitate, e un skill direct util oricui scrie SQL serios pentru analize.

Nu trebuie să devii DBA. Trebuie să înțelegi destul cât să recunoști când e cazul să intervii.

indexuri SQL: Ce e, de fapt, un index

În principiu, un index e o structură de date paralelă pe care motorul bazei o construiește pentru a accelera căutările. Pe scurt: în loc să scaneze tot tabelul rând cu rând, motorul folosește indexul ca pe un cuprins.

Analogie banală dar utilă: o carte de 800 de pagini fără cuprins. Vrei să afli pe ce pagină se vorbește despre „inflația din anii ’80”. Fără cuprins, frunzărești pagină cu pagină. Cu cuprins, ajungi în 3 secunde. Cuprinsul nu modifică conținutul cărții — doar te duce mai rapid la el. Indexul în baza de date e exact asta, pentru rândurile dintr-un tabel.

În spate, structura cea mai folosită e B-tree (Balanced Tree). E arbore binar echilibrat care permite căutarea, inserția și ștergerea în timp logaritmic — pentru un tabel cu 10 milioane de rânduri, asta înseamnă maxim ~23 de comparații pentru a găsi o valoare. Versus 10 milioane fără index.

Când ajută cu adevărat

Indexurile nu sunt magie universală. Ajută în câteva scenarii foarte specifice:

WHERE pe coloane filtrate frecvent. SELECT * FROM orders WHERE customer_id = 12345 beneficiază imens de un index pe customer_id, mai ales pe un tabel cu zeci de milioane de comenzi.

JOIN-uri. Toate coloanele care apar în condiții ON între tabele mari ar trebui să aibă index. Un JOIN între un tabel de 5M rânduri și unul de 50M rânduri, fără index pe cheia de join, e lent pentru orice configurație hardware.

ORDER BY pe coloane sortate des. Dacă sortezi mereu un raport după created_at DESC, un index pe acea coloană elimină pasul de sortare la runtime.

GROUP BY pe coloane grupate frecvent. Aceeași logică — motorul poate folosi indexul ca să parcurgă rândurile deja ordonate, fără hashing intermediar.

În contrast, indexurile nu ajută aproape deloc când:

  • Returnezi un procent mare din tabel (peste ~20% din rânduri — în multe cazuri full table scan e mai eficient)
  • Coloana indexată are foarte puține valori distincte (de exemplu, o coloană booleană sau un câmp status cu 3 valori posibile pe un tabel cu milioane de rânduri)
  • Folosești funcții pe coloana indexată în WHERE: WHERE YEAR(created_at) = 2026 ignoră complet un index pe created_at
  • Folosești LIKE '%text%' — indexul B-tree nu poate ajuta la căutare în interior

Costurile invizibile

Aici e partea pe care 90% din analiștii care „cer un index” o ignoră: indexurile nu sunt gratuite.

Fiecare index costă în trei direcții:

Spațiu pe disk. Un index ocupă în general 10-30% din dimensiunea tabelului indexat. Pentru un tabel de 100 GB, fiecare index suplimentar adaugă 10-30 GB de storage. Cinci indexuri pe același tabel pot dubla spațiul total.

INSERT, UPDATE și DELETE mai lente. La fiecare operație de scriere, motorul trebuie să actualizeze nu doar tabelul, ci și toate indexurile pe el. Un INSERT pe un tabel cu 8 indexuri costă aproximativ de 5-8 ori mai mult decât pe un tabel fără indexuri. Pentru un sistem OLTP cu trafic mare, asta poate fi decisiv.

Plan de execuție complicat. Mai multe indexuri = mai multe opțiuni pe care optimizatorul SQL trebuie să le evalueze. În cazuri rare, asta duce la planuri suboptime alese accidental.

Implicația practică: pe un tabel folosit intens de aplicații (e-commerce, CRM, ERP), adăugarea unui index trebuie justificată. Nu cere niciodată „să adăugăm un index pe X” fără să discuți cu echipa care administrează baza despre impactul asupra scrierilor.

Tipuri de index pe scurt

Cele patru tipuri pe care un analist le întâlnește în practică:

B-tree (cel standard). 95% din cazurile reale. Funcționează pentru egalitate, range queries (<, >, BETWEEN), ORDER BY, JOIN. E default-ul în PostgreSQL, MySQL InnoDB, SQL Server.

Hash. Util doar pentru egalitate strictă (=, IN). Mai rapid decât B-tree pentru lookup-uri exact, dar inutil pentru range. PostgreSQL îl suportă, MySQL îl folosește intern în engine-ul MEMORY. În practică pentru analytics, rar îl alegi conștient.

Bitmap. Specializat pentru coloane cu cardinalitate mică (gen, status, regiune) pe tabele mari de tip data warehouse. Excelent în Oracle, mai rar disponibil în PostgreSQL sau MySQL. Pe analytics workloads cu multe WHERE-uri combinate, oferă câștiguri spectaculoase.

Full-text. Pentru căutări în text (LIKE-style avansat, MATCH-uri). PostgreSQL are tsvector + GIN, MySQL are FULLTEXT. Util când construiești dashboard-uri cu search liber pe câmpuri descriptive.

Pentru cele mai multe nevoi de analytics, B-tree-ul standard e suficient. Restul intervin în cazuri specifice.

Indexuri compuse — unde se ascunde câștigul

Un index pe o singură coloană e simplu. Un index compus (pe mai multe coloane) e unde lucrurile devin interesante și unde majoritatea analyștilor pierd performanță.

Regula: ordinea coloanelor în index contează. Mult.

Imaginează un index pe (customer_id, order_date). Acest index ajută la query-uri care filtrează pe customer_id, sau pe customer_id + order_date împreună. Dar nu ajută la query-uri care filtrează doar pe order_date. Indexul nu e o cutie cu valori interschimbabile — e o structură arborescentă ordonată întâi după prima coloană.

Regulă practică pentru a alege ordinea coloanelor într-un index compus:

  1. Coloanele cu filtre de egalitate înainte (cele care apar în WHERE coloana = ceva)
  2. După, coloanele cu range queries (WHERE coloana BETWEEN … sau coloana > …)
  3. La final, coloanele folosite în ORDER BY sau GROUP BY

Pentru un query de tipul SELECT * FROM orders WHERE store_id = 5 AND order_date >= '2026-01-01' ORDER BY total DESC — indexul ideal e pe (store_id, order_date, total). În această ordine.

Cum verifici dacă un index e folosit

Modul cel mai practic: EXPLAIN (PostgreSQL, MySQL) sau SET STATISTICS IO + plan grafic (SQL Server).

În PostgreSQL:

EXPLAIN ANALYZE
SELECT * FROM orders
WHERE customer_id = 12345
AND order_date >= '2026-01-01';

În output urmărești două lucruri: tipul de scan (Index Scan / Index Only Scan = bun, Seq Scan pe tabel mare = problematic) și timpul de execuție. Pentru query-uri lente, planul îți spune unde se pierde timpul.

În MySQL e similar prin EXPLAIN, cu o coloană key care îți arată ce index a fost folosit (sau NULL dacă niciunul).

Detaliile complete pentru EXPLAIN în PostgreSQL sunt în documentația oficială. E ceva ce orice analist care lucrează cu Postgres serios ar trebui să citească măcar o dată.

Index lipsă vs index nefolosit

Două diagnostice opuse care apar des în audit-uri de performanță:

Index lipsă. Un query care face Seq Scan pe un tabel de 50M rânduri pentru a returna 200 de rânduri. Plus un index ar transforma 12 secunde în 80 milisecunde. Aici intervenția e clară.

Index nefolosit. Un index existent care nu apare în niciun plan de execuție în ultima lună. PostgreSQL îl raportează prin pg_stat_user_indexes (coloana idx_scan = 0 înseamnă neutilizat). MySQL prin sys.schema_unused_indexes. Aceste indexuri sunt cost net — ocupă spațiu, încetinesc scrierile, dar nu aduc beneficii. Eliminarea lor periodică e o practică sănătoasă.

În experiența reală a echipelor mature, audit-ul trimestrial de indexuri arată tipic: 5-10% sunt indexuri nefolosite care se elimină, iar 2-5 indexuri lipsă se descoperă din analiza query-urilor lente. Asta menține baza eficientă fără intervenții dramatice.

Indexuri pe materialized views și partitioning

Pentru analytics serios pe volume mari, indexurile clasice se completează cu două tehnici adiacente.

Materialized views — în PostgreSQL și Oracle, sunt rezultate de query-uri pre-calculate și stocate pe disk. Pentru un raport executiv care agregă 80M rânduri în 12 cifre, e mult mai eficient să rulezi refresh-ul materialized view o dată pe noapte decât să recalculezi la fiecare deschidere.

Partitioning — împărțirea unui tabel mare pe partiții (de obicei pe an, lună, regiune). Query-urile care filtrează pe coloana de partiție ating doar partițiile relevante. Pentru tabele de log-uri sau tranzacții cu istoric lung, partitioning + indexuri locale pe fiecare partiție e abordarea standard în 2026.

Aceste două tehnici nu sunt jobul tipic al unui analist, dar e bine să știi că există atunci când discuți cu echipa de date despre un raport care „rulează prea încet”.

Diferențe între engine-uri

Pe scurt, comportamente diferite care pot surprinde:

PostgreSQL: B-tree default. Hash, GIN, GiST, BRIN disponibile pentru cazuri specifice. Mature pe planning, generos cu opțiunile.

MySQL InnoDB: B-tree (de fapt, B+ tree) default. Cheia primară e mereu un clustered index — datele sunt fizic ordonate pe ea. Important: indexurile secundare în InnoDB stochează valoarea primary key-ului, nu pointerul direct la rând. Asta înseamnă că primary keys lungi (de exemplu UUID-uri ca PK) penalizează toate indexurile.

SQL Server: Similar cu MySQL pe clustered index. Diferența majoră e în plan caching și statistics — SQL Server actualizează automat statistici în multe scenarii, ceea ce ajută la planuri bune fără intervenție.

Snowflake, BigQuery, Redshift: Indexurile clasice aproape lipsesc sau funcționează diferit. Aceste data warehouses analytics se bazează pe columnar storage și pe partitioning / clustering keys. Nu mai e despre B-tree, e despre cum scrii query-ul ca să elimini coloane și partiții.

Ce poți cere realist echipei DBA

Ca analist, conversațiile cu DBA sunt mai productive când vii cu cerințe specifice, nu cu plângeri vagi. Pattern care funcționează:

  1. Identifică query-ul problematic concret (cu textul SQL, nu doar „raportul lunar”)
  2. Rulează EXPLAIN și adu planul de execuție
  3. Identifică tabelul scanat integral și coloanele din WHERE / JOIN
  4. Propune un index potențial (chiar dacă nu e perfect — propune ceva)
  5. Întreabă DBA dacă propunerea are sens și ce implicații vede

O cerere de tipul „raportul de vânzări durează 23 de secunde, am rulat EXPLAIN și văd Seq Scan pe orders cu filtru pe store_id; cred că ne-ar ajuta un index pe (store_id, order_date)” e tratată complet diferit față de „raportul rulează prea încet, faceți ceva”.

Lucruri pe care un analist NU ar trebui să facă singur

Pe o bază de date de producție, nu rulezi CREATE INDEX direct fără aprobare. Câteva motive concrete:

  • Crearea unui index pe un tabel mare poate dura ore și blochează scrierile (cu excepția CREATE INDEX CONCURRENTLY în Postgres)
  • Pe MySQL, depinde de versiune, dar pe versiuni mai vechi crearea de indexuri blochează tabelul
  • Conflicte cu alte indexuri existente sau cu deployment-uri în curs

Regula sănătoasă: în mediul de development sau pe replici read-only, experimentează liber. Pe producție, propunerile trec prin proces.

Verificare rapidă a sănătății indexurilor

Ca analist care intră într-un proiect nou, două query-uri merită rulate pe baza ta principală:

Pentru PostgreSQL, indexuri nefolosite:

SELECT schemaname, relname, indexrelname, idx_scan
FROM pg_stat_user_indexes
WHERE idx_scan = 0
ORDER BY pg_relation_size(indexrelid) DESC;

Pentru identificarea celor mai mari tabele și ce indexuri au, există o serie de query-uri standard pe care DBA-ii le folosesc. Ele îți dau o imagine în 10 minute despre starea bazei.

Dacă vezi tabele de 50+ GB cu 10+ indexuri și jumătate dintre ele cu zero utilizare în ultima lună, ai descoperit aproape sigur o problemă de mentenanță care va apărea în alte conversații.

Tema se leagă natural de discuția despre optimizare query, unde am intrat în detaliu pe pattern-urile pe care le observăm în piață. În fond, indexuri SQL nu e doar un concept tehnic — este o decizie de business cu impact direct pe productivitatea echipei.

Trei reguli de aur

Pentru un analist care lucrează cu SQL în 2026:

  1. Întreabă „ce indexuri există pe tabelele cu care lucrez?” în prima săptămână pe un proiect nou. Salvează ore de debug ulterior.
  2. Învață să citești un plan EXPLAIN suficient cât să identifici un Seq Scan problematic. Nu trebuie să-l optimizezi tu, dar trebuie să-l identifici.
  3. Niciodată nu propune un index nou fără să-l discuți cu echipa care deține baza. Costul scrierilor și impactul pe alte query-uri sunt invizibile pentru cineva care vede doar query-ul lui.

Pe restul scalei — indexuri partiționate, indexuri parțiale, indexuri pe expresii — sunt subiecte de DBA. Un analist care le cunoaște la nivel de existență, fără să le opereze, e deja la un nivel peste majoritatea echipelor.

În practică, indexuri SQL a trecut de la subiect de roadmap la prioritate operațională pentru echipele care livrează rezultate de business — exact tipul de tracțiune pe care o vedem reflectată în deciziile reale de buget. Pentru cititorii care lucrează zilnic cu indexuri SQL, articolul rămâne deschis pentru update-uri pe măsură ce piața evoluează.


Întrebări frecvente

Ce este un index, pe scurt?

O structură de date paralelă pe care motorul bazei o construiește ca să accelereze căutările. Analogia utilă: o carte de 800 de pagini fără cuprins. În spate, structura cea mai folosită e B-tree.

Când NU ajută un index?

În patru situații: când returnezi peste circa 20% din rânduri, caz în care un full table scan e adesea mai eficient; când coloana are foarte puține valori distincte, cum e una booleană sau un status cu trei valori; când folosești o funcție pe coloana indexată în WHERE, pentru că WHERE YEAR(created_at) = 2026 ignoră complet indexul pe created_at; și când folosești LIKE cu procent la început, pentru că B-tree nu poate căuta în interiorul textului.

Ce costă un index?

Trei lucruri pe care le ignoră majoritatea celor care „cer un index”: spațiu pe disc, operații INSERT, UPDATE și DELETE mai lente, și un plan de execuție mai complicat. Pe un tabel folosit intens de aplicații — e-commerce, CRM, ERP — adăugarea unui index trebuie justificată, nu presupusă.

Lasă un răspuns

Adresa ta de email nu va fi publicată. Câmpurile obligatorii sunt marcate cu *

Politica de confidențialitate · Politica de cookie-uri