eduardweb.
PostgreSQLIntermediar#performance#postgresql#backend#database

Postgres JSONB: Când merită să renunți la tabelele clasice și cum îl indexezi cu GIN

De Cristian Barbu, 2 iul. 2026 · 14 vizualizări · 3 like-uri

Postat 2 iul. 2026
sql
-- Crearea tabelului cu coloană JSONB
CREATE TABLE users (
    id SERIAL PRIMARY KEY,
    email VARCHAR(255) UNIQUE,
    metadata JSONB
);

-- Crearea indexului GIN folosind jsonb_path_ops (mai rapid și mai mic pe disc)
CREATE INDEX idx_users_metadata_path_ops ON users USING gin (metadata jsonb_path_ops);

-- Query-ul corect care folosește indexul GIN (operatorul de incluziune)
SELECT * FROM users 
WHERE metadata @> '{"preferences": {"theme": "dark"}}';

-- Query-ul care NU va folosi acest index GIN specific:
-- SELECT * FROM users WHERE metadata->'preferences'->>'theme' = 'dark';

Am văzut prea des oameni care folosesc JSONB în Postgres doar pentru că le-a fost lene să facă o migrare calumea. E o capcană în care am picat și eu acum vreo opt ani, la un proiect cu peste 15.000 de utilizatori activi, unde am zis că "lasă, punem setările și profilul într-un JSON că e mai flexibil". Mare greșeală, ne-am chinuit luni de zile cu query-uri lente și blocaje.

De atunci, am învățat că JSONB e o unealtă genială, dar numai dacă știi exact când să o folosești și, mai ales, cum să o indexezi ca să nu îți pui baza de date în cap.

Când are sens JSONB și care e prețul plătit

Regula mea de aur e simplă: folosește coloane normale (text, int, boolean) pentru tot ce înseamnă date structurate, entități de bază și câmpuri pe care faci join-uri frecvente.

JSONB strălucește în două scenarii:

  1. Atribute dinamice: De exemplu, într-un magazin online unde vinzi și televizoare (care au diagonală, rezoluție) și haine (care au mărime, material). E un coșmar să faci tabele de legătură EAV (Entity-Attribute-Value) pentru asta.
  2. Payload-uri de la API-uri externe: Când integrezi un Stripe sau un webhook extern și vrei să salvezi exact ce ți-au trimis ei, fără să știi dinainte dacă vor adăuga câmpuri noi luna viitoare.

Dar există un trade-off major. JSONB consumă mult mai mult spațiu pe disc decât coloanele clasice. În plus, orice update pe o proprietate din interiorul JSON-ului forțează Postgres să scrie din nou tot documentul (datorită modului în care funcționează MVCC). Dacă ai update-uri dese pe câmpuri din JSONB, o să ai probleme mari cu disk I/O-ul.

Magia indexării: GIN în loc de B-Tree

Dacă ai o tabelă cu 2 milioane de rânduri și cauți după o cheie din interiorul JSON-ului, Postgres va face un sequential scan de toată frumusețea. Aici intră în scenă indexul GIN (Generalized Inverted Index).

Spre deosebire de un index B-Tree clasic (care e bun pentru valori unice și ordonate), GIN "despachetează" documentul JSONB și creează intrări pentru fiecare cheie și valoare din interior.

Avem două moduri mari de indexare cu GIN:

  • jsonb_ops (cel default): Indexează absolut tot (chei, valori, sub-obiecte). E flexibil, dar indexul devine uriaș pe disc.
  • jsonb_path_ops: Indexează doar perechile cheie-valoare sub formă de hash. Indexul e mult mai mic (am salvat și 40% din spațiu pe unele proiecte) și căutările sunt mai rapide, dar ești limitat la operatorul de incluziune (@>).

La un proiect recent, un query neindexat pe un JSONB de 1.2 milioane de rânduri dura cam 850ms. După ce am adăugat un index GIN cu jsonb_path_ops, același query a scăzut la 3.4ms. Diferența e de la cer la pământ.

Cum scrii query-ul ca să folosească indexul

Marea capcană aici este operatorul folosit. Dacă scrii un query folosind operatorul săgeată (->> sau ->), Postgres s-ar putea să ignore indexul GIN complet. Cel mai sigur mod de a te asigura că indexul e folosit este să folosești operatorul de incluziune @>. Acest operator verifică dacă JSON-ul din stânga conține structura din dreapta.

Puteți vedea în exemplul de cod cum se creează indexul optimizat și cum se scrie query-ul corect pentru a profita de el.

Voi cum folosiți JSONB? Îl țineți doar pentru loguri și chestii secundare, sau aveți bucăți din core-ul aplicației care se bazează pe el?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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