eduardweb.
PostgreSQLIntermediar#performance#postgresql#databases#sql

Postgres JSONB: Când merită să renunți la coloane și cum pui index GIN

De Liliana Ghiță, 2 iun. 2026 · 3 vizualizări · 3 like-uri

Postat 2 iun. 2026
sql
-- Creăm tabelul de produse cu coloană JSONB
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    attributes JSONB
);

-- Creăm un index GIN optimizat folosind jsonb_path_ops
CREATE INDEX idx_products_attributes_path ON products USING gin (attributes jsonb_path_ops);

-- Query rapid care folosește indexul de mai sus
SELECT name 
FROM products 
WHERE attributes @> '{"specs": {"ram": "16GB"}}';

Am văzut prea des greșeala asta în ultimii ani: „Hai să punem totul în JSONB că e mai simplu, nu mai facem migrații”. Am pățit-o și eu acum vreo 5 ani la un proiect cu peste 120.000 de produse unde specificațiile tehnice variau enorm de la o categorie la alta. Am zis că facem schema-less în Postgres și gata. Spoiler: ne-am lovit cu capul de pragul de sus destul de repede.

JSONB este o unealtă superbă, dar vine cu un cost masiv dacă este folosită ca înlocuitor leneș pentru tabele normalizate.

Când treci pe JSONB și când rămâi la coloane clasice

Regula mea de aur e simplă: dacă ai nevoie de constrângeri de integritate (foreign keys, NOT NULL, check constraints) sau dacă actualizezi des acele valori, folosește coloane clasice.

Am avut un caz unde un coleg a pus statusul unei comenzi și prețul într-un câmp JSONB numit metadata. La 10.000 de update-uri pe oră, baza de date începuse să facă write amplification masiv. De ce? Postgres nu poate face update la o singură cheie din JSONB în mod direct pe disc. El rescrie tot rândul și tot documentul JSONB. Gândește-te la uzura pe SSD-uri și la bloat-ul din tabele.

În schimb, JSONB strălucește în alte scenarii:

  • Date polimorfe (cum sunt atributele produselor: un laptop are RAM și CPU, o cămașă are mărime și material).
  • Integrare cu API-uri externe unde structura se schimbă des și vrei doar să stochezi payload-ul pentru audit.
  • Setări de utilizator pe care le citești la fiecare request, dar le scrii extrem de rar.

Indexarea GIN: Salvarea query-urilor tale

Dacă ai un JSONB de 50KB și cauți prin el cu operatorul ->> fără index, Postgres va face un sequential scan de toată frumusețea. La 1 milion de rânduri, procesorul tău o să stea în 100% constant.

Aici intră în scenă indexul GIN (Generalized Inverted Index). Acesta creează intrări pentru fiecare cheie și valoare din interiorul documentului tău JSONB.

Există două tipuri de clase de operatori pentru GIN:

  1. jsonb_ops (default) - indexează tot: chei, valori și sub-chei. E flexibil, dar e mare pe disc.
  2. jsonb_path_ops - indexează doar căile și valorile (ex: {"user": {"id": 1}}). Este mult mai mic ca dimensiune și mai rapid pentru query-uri cu operatorul @> (contine), dar nu te ajută dacă vrei să verifici doar dacă o cheie există.

La proiectul de e-commerce menționat, trecerea de la un index GIN implicit la jsonb_path_ops ne-a redus dimensiunea indexului cu 40% și a tăiat timpul de execuție al căutărilor de la 400ms la sub 5ms.

Cum folosești asta în practică? Ai exemplul de cod alături. Folosim operatorul @> pentru că este cel mai bine optimizat pentru indexul GIN.

Tu ce experiențe ai avut cu JSONB? L-ai folosit vreodată ca o scuză să nu mai desenezi schema bazei de date și ai regretat mai târziu?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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