eduardweb.
PostgreSQLIntermediar#performance#postgresql#database#sql

Postgres JSONB cu cap: Când renunțăm la tabelele clasice și cum indexezi cu GIN

De Corina Dobre, 2 iul. 2026 · 15 vizualizări · 2 like-uri

Postat 2 iul. 2026
sql
-- Crearea tabelului cu coloană JSONB
CREATE TABLE products (
    id SERIAL PRIMARY KEY,
    name VARCHAR(255) NOT NULL,
    attributes JSONB
);

-- Crearea unui index GIN eficient folosind jsonb_path_ops
CREATE INDEX idx_products_attributes ON products USING gin (attributes jsonb_path_ops);

-- Query rapid folosind operatorul "contains" (@>)
SELECT name, attributes->>'brand' AS brand
FROM products
WHERE attributes @> '{"type": "laptop", "specs": {"ram": "16GB"}}';

Am văzut prea mulți devi care dau în extrema JSONB doar pentru că le e lene să facă o migrare sau, dimpotrivă, se chinuie cu zeci de tabele de legătură când un simplu câmp JSONB rezolva problema imediat. Postarea asta e un ghid rapid, bazat pe ce am lovit eu în producție, despre cum să folosești JSONB fără să îți pui baza de date în cap și cum să o indexezi corect.

Când facem schema clasică și când trecem pe JSONB?

Regula mea de aur e simplă: dacă ai nevoie de validare strictă, foreign keys sau dacă acele date sunt pivotul aplicației tale, mergi pe coloane clasice. Am avut un proiect cu vreo 15.000 de useri unde cineva stocase adresele de facturare în JSONB. A fost un coșmar când a trebuit să facem rapoarte financiare și să curățăm datele introduse greșit (ba lipsea țara, ba codul poștal era scris ca integer, ba ca string).

În schimb, JSONB e aur curat pentru date dinamice. Gândește-te la specificații de produse într-un magazin online. Un laptop are RAM și procesor, un tricou are mărime și material, o sticlă de vin are an și soi. Să faci tabele de tip Entity-Attribute-Value (EAV) e o durere de cap masivă și distruge performanța la JOIN-uri. Aici pui o coloană attributes de tip JSONB și ai rezolvat problema elegant. Un alt caz excelent sunt payload-urile de la webhook-uri externe unde structura se poate schimba des și nu vrei să rulezi migrări în fiecare săptămână.

Indexarea GIN – Salvarea performanței

Dacă scrii SELECT * FROM table WHERE data->>'key' = 'value' pe un tabel mare fără index, Postgres va face un full table scan. La un proiect cu 4 milioane de rânduri de log-uri, un astfel de query ne lua peste 8 secunde. După ce am pus un index GIN, timpul a scăzut la sub 5 milisecunde.

Avem două opțiuni mari pentru indexarea GIN:

  1. jsonb_ops (default): Indexează tot – chei, valori și sub-chei. E flexibil, dar ocupă mult spațiu pe disc și scrierile sunt mai lente.
  2. jsonb_path_ops: Indexează doar perechile cheie-valoare sub formă de hash. Este mult mai mic ca dimensiune (am economisit cam 40% din spațiul indexului pe disc) și mai rapid la căutări, dar ești limitat la interogări care folosesc operatorul @> (contains).

Trade-off-ul sincer

Plătești un preț destul de mare pentru flexibilitatea asta. JSONB consumă considerabil mai mult spațiu de stocare decât coloanele normale din cauza metadatelor salvate pentru fiecare rând. De asemenea, update-urile pe JSONB sunt foarte costisitoare. Postgres folosește MVCC, deci la fiecare update pe o cheie din JSON, el scrie de fapt o nouă copie a întregului rând pe disc. Dacă ai un câmp pe care îl actualizezi frecvent (de exemplu, un contor de vizualizări), nu îl pune niciodată în JSONB.

Voi cum gestionați datele semistructurate? Mergeți pe varianta hibridă sau preferați să normalizați tot până la refuz?

Răspunsuri 0

Se încarcă răspunsurile…

Loghează-te pentru a răspunde

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