eduardweb.
PostgreSQLIntermediar#postgresql#backend#database#jsonb

Postgres JSONB: Când merită să spargi schema clasică și cum pui index GIN să nu crape baza

De Bogdan Răducanu, 7 iun. 2026 · 1 vizualizări · 2 like-uri

Postat acum 2 zile
sql
-- Crearea indexului optimizat pe coloana JSONB folosind path_ops
CREATE INDEX idx_produse_atribute ON produse USING gin (atribute jsonb_path_ops);

-- Query rapid care folosește indexul (operatorul @> înseamnă "conține")
SELECT nume, pret 
FROM produse 
WHERE atribute @> '{"specificatii": {"culoare": "negru"}}';

Am văzut prea des oameni care trec la Postgres și încep să arunce totul în JSONB doar pentru că e „flexibil”. Am făcut și eu prostia asta acum vreo 6 ani la un proiect și am regretat rapid. JSONB are un loc bine definit, dar nu e un înlocuitor pentru o schemă corect relațională.

Când merită să folosești JSONB?

Regula mea de aur e simplă: folosește JSONB doar când structura datelor tale este dictată de factori externi pe care nu-i poți controla sau când ai atribute extrem de dinamice.

Am avut un proiect cu vreo 12.000 de produse active în magazin. Fiecare categorie avea atribute complet diferite: hainele aveau mărimi și culori, laptopurile aveau RAM și procesor, iar loțiunile de plajă aveau factor de protecție. Dacă mergeam pe clasicul EAV (Entity-Attribute-Value), aveam nevoie de join-uri complexe care puneau baza de date în cap la query-uri mai grele.

Aruncând aceste specificații într-o coloană atribute de tip JSONB, am simplificat masiv codul și am salvat timp de dezvoltare. A mers brici pentru că datele erau doar de citit și afișat, fără validări complexe de integritate.

Trade-off-ul sincer: unde te frigi cu JSONB

Nu există magie gratis. Înainte să pui jsonb peste tot, ține cont de problemele astea:

  1. Spațiu pe disc: JSONB stochează cheile pentru fiecare rând în parte. Dacă ai un milion de rânduri și cheia se numește "specificatie_tehnica_detaliata", o să stochezi stringul ăsta de un milion de ori. Coloanele normale stochează metadatele o singură dată în definirea tabelei.
  2. Update-uri scumpe: Postgres folosește MVCC. Când faci update la o singură valoare dintr-un document JSONB mare, Postgres scrie o copie nouă a întregului rând pe disc. Dacă ai JSON-uri mari și update-uri dese, scrii pe disc de te doare capul.
  3. Lipsa constrângerilor: N-ai foreign keys în interiorul JSON-ului. Dacă ai un ID de user acolo și userul e șters, rămâi cu date orfane.

Indexarea GIN: Cum evităm Sequential Scan

Dacă ai trecut de 100.000 de rânduri și cauți în JSONB fără index, baza de date va face un scan complet pe tabelă. Pentru query-uri rapide, ai nevoie de un index GIN (Generalized Inverted Index).

Sunt două moduri mari în care poți defini un index GIN pe JSONB:

  • jsonb_ops (default): Permite query-uri flexibile. Poți căuta după chei, după valori sau după existența unei chei. E destul de mare ca dimensiune.
  • jsonb_path_ops: E mult mai optimizat. Indexul ocupă cu vreo 30-50% mai puțin spațiu pe disc și e mai rapid la căutări de tip cheie-valoare, dar nu te lasă să verifici dacă o cheie există independent de valoarea ei.

La proiectul menționat mai sus, trecerea de la jsonb_ops la jsonb_path_ops pe coloana de atribute ne-a redus dimensiunea indexului de la 4.2 GB la 2.5 GB și a scăzut timpul de răspuns la căutări sub 5ms.

Voi cum gestionați datele dinamice în proiecte? Ați mers pe JSONB sau ați rămas fani ai tabelelor pivot clasice?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

Doar membrii comunității pot lăsa comentarii.