{"id":52584,"date":"2019-11-12T00:00:00","date_gmt":"2019-11-11T21:00:00","guid":{"rendered":"https:\/\/prohoster.info\/blog\/blog_prohoster\/zamena-eav-na-jsonb-v-postgresql"},"modified":"2020-02-18T14:00:21","modified_gmt":"2020-02-18T11:00:21","slug":"zamena-eav-na-jsonb-v-postgresql","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","title":{"rendered":"Sostituzione di EAV con JSONB in PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<blockquote><p>TL; DR: JSONB pu\u00f2 semplificare notevolmente lo sviluppo dello schema del database senza compromettere le prestazioni delle query.<\/p><\/blockquote>\n<p><\/p>\n<h3>Introduzione<\/h3>\n<p>\nPrendiamo un esempio classico, forse uno dei pi\u00f9 antichi casi d'uso nel mondo dei database relazionali (database): abbiamo un'entit\u00e0 e dobbiamo salvare determinate propriet\u00e0 (attributi) di quell'entit\u00e0. Ma non tutti gli esemplari possono avere lo stesso insieme di propriet\u00e0; inoltre, in futuro, potrebbero essere aggiunte ulteriori propriet\u00e0.<\/p>\n<p>Il modo pi\u00f9 semplice per risolvere questo problema \u00e8 creare una colonna nella tabella del database per ogni valore di propriet\u00e0 e semplicemente riempire quelle necessarie per un determinato esemplare dell'entit\u00e0. Ottimo! Problema risolto... fino a quando la tua tabella non conterr\u00e0 milioni di registrazioni e non avrai bisogno di aggiungere un nuovo record.<\/p>\n<p>Consideriamo il pattern EAV (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">Entity-Attribute-Value<\/a><\/noindex>), viene abbastanza frequentemente. Una tabella contiene entit\u00e0 (record), un'altra tabella contiene i nomi delle propriet\u00e0 (attributi), e la terza tabella collega le entit\u00e0 ai loro attributi, contenendo il valore di questi attributi per l'entit\u00e0 attuale. Questo ti consente di avere diversi insiemi di propriet\u00e0 per diversi oggetti e di aggiungere propriet\u00e0 \u201cal volo\u201d, senza modificare le strutture del database.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\n\u0422\u0435\u043c \u043d\u0435 \u043c\u0435\u043d\u0435\u0435, \u044f \u0431\u044b \u043d\u0435 \u043f\u0438\u0441\u0430\u043b \u044d\u0442\u0443 \u0437\u0430\u043c\u0435\u0442\u043a\u0443, \u0435\u0441\u043b\u0438 \u0431\u044b \u043d\u0435 \u0431\u044b\u043b\u043e \u043d\u0435\u0434\u043e\u0441\u0442\u0430\u0442\u043a\u043e\u0432 \u0432 \u043f\u043e\u0434\u0445\u043e\u0434\u0435 \u0441 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u0435\u043c EVA. \u0422\u0430\u043a, \u043d\u0430\u043f\u0440\u0438\u043c\u0435\u0440, \u0434\u043b\u044f \u043f\u043e\u043b\u0443\u0447\u0435\u043d\u0438\u044f \u043e\u0434\u043d\u043e\u0439 \u0438\u043b\u0438 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0435\u0439, \u043a\u043e\u0442\u043e\u0440\u044b\u0435 \u0438\u043c\u0435\u044e\u0442 \u043f\u043e 1 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u0443 \u0442\u0440\u0435\u0431\u0443\u0435\u0442\u0441\u044f 2 join&#8217;\u0430 (\u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u044f) \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0435: \u043f\u0435\u0440\u0432\u044b\u0439 \u2013 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u043e\u0432, \u0432\u0442\u043e\u0440\u043e\u0439 \u2013 \u043e\u0431\u044a\u0435\u0434\u0438\u043d\u0435\u043d\u0438\u0435 \u0441 \u0442\u0430\u0431\u043b\u0438\u0446\u0435\u0439 \u0437\u043d\u0430\u0447\u0435\u043d\u0438\u0439. \u0415\u0441\u043b\u0438 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c \u0438\u043c\u0435\u044e\u0442 2 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b, \u0442\u043e \u043d\u0443\u0436\u043d\u043e \u0443\u0436\u0435 4 join&#8217;\u0430! \u041a\u0440\u043e\u043c\u0435 \u0442\u043e\u0433\u043e, \u0432\u0441\u0435 \u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b \u043e\u0431\u044b\u0447\u043d\u043e \u0445\u0440\u0430\u043d\u044f\u0442\u0441\u044f \u0432 \u0432\u0438\u0434\u0435 \u0441\u0442\u0440\u043e\u043a, \u0447\u0442\u043e \u043f\u0440\u0438\u0432\u043e\u0434\u0438\u0442 \u043a \u043f\u0440\u0438\u0432\u0435\u0434\u0435\u043d\u0438\u044e \u0442\u0438\u043f\u043e\u0432, \u043a\u0430\u043a \u0434\u043b\u044f \u0440\u0435\u0437\u0443\u043b\u044c\u0442\u0430\u0442\u0430, \u0442\u0430\u043a \u0438 \u0434\u043b\u044f \u0443\u0441\u043b\u043e\u0432\u0438\u044f WHERE. \u0415\u0441\u043b\u0438 \u0432\u044b \u043f\u0438\u0448\u0435\u0442\u0435 \u043c\u043d\u043e\u0433\u043e \u0437\u0430\u043f\u0440\u043e\u0441\u043e\u0432, \u0442\u043e \u044d\u0442\u043e \u0434\u043e\u0441\u0442\u0430\u0442\u043e\u0447\u043d\u043e \u0440\u0430\u0441\u0442\u043e\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e, \u0441 \u0442\u043e\u0447\u043a\u0438 \u0437\u0440\u0435\u043d\u0438\u044f \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0440\u0435\u0441\u0443\u0440\u0441\u043e\u0432.<\/p>\n<p>Nonostante questi evidenti svantaggi, l'EAV \u00e8 stato a lungo utilizzato per risolvere questo tipo di problemi. Questi difetti erano inevitabili e semplicemente non c'era un'alternativa migliore. <br \/>\nMa poi in PostgreSQL \u00e8 arrivata una nuova 'tecnologia'\u2026<\/p>\n<p>A partire da PostgreSQL 9.4, \u00e8 stato introdotto il tipo di dati JSONB per memorizzare dati JSON in formato binario. Sebbene la memorizzazione di JSON in questo formato richieda in genere un po' pi\u00f9 di spazio e tempo rispetto al semplice JSON testuale, le operazioni su di esso sono molto pi\u00f9 veloci. Inoltre, JSONB supporta l'indicizzazione, rendendo ancora pi\u00f9 rapide le query.<\/p>\n<p>Il tipo di dati JSONB ci consente di sostituire il pesante pattern EAV aggiungendo semplicemente una colonna JSONB nella nostra tabella delle entit\u00e0, semplificando notevolmente il design del database. Ma molti sostengono che ci\u00f2 debba comportare una diminuzione delle prestazioni\u2026 \u00c8 per questo motivo che \u00e8 emerso questo articolo.<\/p>\n<h3>Impostazione di un database di test<\/h3>\n<p>\nPer questa comparazione ho creato un database su una nuova installazione di PostgreSQL 9.5 su un server da 80 dollari. <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex> Ubuntu 14.04. Dopo aver configurato alcuni parametri in postgresql.conf, ho avviato <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/coussej\/80c385332ce37df6687f\">questo<\/a><\/noindex> script utilizzando psql. Sono state create le seguenti tabelle per rappresentare i dati in forma EAV:<\/p>\n<pre><code class=\"pgsql\">CREATE TABLE entity ( \n  id           SERIAL PRIMARY KEY, \n  name         TEXT, \n  description  TEXT\n);\nCREATE TABLE entity_attribute (\n  id          SERIAL PRIMARY KEY, \n  name        TEXT\n);\nCREATE TABLE entity_attribute_value (\n  id                  SERIAL PRIMARY KEY, \n  entity_id           INT    REFERENCES entity(id), \n  entity_attribute_id INT    REFERENCES entity_attribute(id), \n  value               TEXT\n);\n<\/code><\/pre>\n<p>\nDi seguito \u00e8 riportata la tabella in cui verranno memorizzati gli stessi dati, ma con gli attributi nella colonna di tipo JSONB \u2013 <i>properties<\/i>.<\/p>\n<pre><code class=\"pgsql\">CREATE TABLE entity_jsonb (\n  id          SERIAL PRIMARY KEY, \n  name        TEXT, \n  description TEXT,\n  properties  JSONB\n);\n<\/code><\/pre>\n<p>\nSembra molto pi\u00f9 semplice, vero? Quindi sono stati aggiunti 10 milioni di record nelle tabelle delle entit\u00e0 (<i>entity<\/i> &amp; <i>entity_jsonb<\/i>) e di conseguenza, sono stati riempiti con dati identici le tabelle in cui viene utilizzato il pattern EAV e l'approccio con la colonna JSONB \u2013 <i>entity_jsonb.properties<\/i>. In questo modo, abbiamo ottenuto diversi tipi di dati all'interno dell'intero set di propriet\u00e0. Ecco un esempio di dati:<\/p>\n<pre><code class=\"json\">{\n  id:          1\n  name:        \"Entity1\"\n  description: \"Test entity n. 1\"\n  properties:  {\n    color:        \"red\"\n    lenght:       120\n    width:        3.1882420\n    hassomething: true\n    country:      \"Belgium\"\n  } \n}<\/code><\/pre>\n<p>\nQuindi, ora abbiamo dati identici per entrambe le opzioni. Iniziamo a confrontare le implementazioni in azione!<\/p>\n<h3>Semplificazione del design<\/h3>\n<p>\nIn precedenza si \u00e8 gi\u00e0 detto che il design del database \u00e8 stato notevolmente semplificato: una tabella, grazie all'uso di una colonna JSONB per le propriet\u00e0, anzich\u00e9 tre tabelle per EAV. Ma come si riflette questo nelle query?<\/p>\n<pre><code class=\"pgsql\">-- EAV\nUPDATE entity_attribute_value \nSET value = 'blue' \nWHERE entity_attribute_id = 1 \n  AND entity_id = 120;\n\n-- JSONB\nUPDATE entity_jsonb \nSET properties = jsonb_set(properties, '{\"color\"}', '\"blue\"') \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nCome possiamo vedere, l'ultima query non appare pi\u00f9 semplice. Per aggiornare il valore di una propriet\u00e0 nell'oggetto JSONB, dobbiamo usare la funzione <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/functions-json.html\">jsonb_set()<\/a><\/noindex>, e dobbiamo passare il nostro nuovo valore come oggetto JSONB. Tuttavia, non abbiamo bisogno di conoscere alcun identificatore in anticipo. Guardando l'esempio con EAV, dobbiamo sapere sia entity_id che entity_attribute_id per eseguire l'aggiornamento. Se desideriamo aggiornare una propriet\u00e0 nella colonna JSONB in base al nome dell'oggetto, tutto questo pu\u00f2 essere fatto con una semplice riga.<\/p>\n<p>Ora scegliamo l'entit\u00e0 che abbiamo appena aggiornato in base al suo nuovo colore:<\/p>\n<pre><code class=\"pgsql\">-- EAV\nSELECT e.name \nFROM entity e \n  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id\n  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id\nWHERE ea.name = 'color' AND eav.value = 'blue';\n\n-- JSONB\nSELECT name \nFROM entity_jsonb \nWHERE properties -&gt;&gt; 'color' = 'blue';\n<\/code><\/pre>\n<p>\nRitengo che possiamo concordare sul fatto che il secondo \u00e8 pi\u00f9 breve (senza join!), e quindi pi\u00f9 leggibile. Qui vince JSONB! Utilizziamo l'operatore JSON -&gt;&gt; per ottenere il colore come valore di testo dall'oggetto JSONB. Esiste anche un secondo modo per raggiungere lo stesso risultato nel modello JSONB utilizzando l'operatore @&gt;:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name \nFROM entity_jsonb \nWHERE properties @&gt; '{\"color\": \"blue\"}';\n<\/code><\/pre>\n<p>\n\u00c8 un po' pi\u00f9 complicato: stiamo controllando se l'oggetto JSON nella colonna delle propriet\u00e0 contiene l'oggetto a destra dell'operatore @&gt;. Meno leggibile, pi\u00f9 performante (vedi oltre). <\/p>\n<p>Semplifichiamo ulteriormente l'uso di JSONB quando \u00e8 necessario selezionare pi\u00f9 propriet\u00e0 contemporaneamente. Qui \u00e8 dove l'approccio JSONB si adatta meglio: selezioniamo semplicemente le propriet\u00e0 come colonne aggiuntive nel nostro set di risultati senza dover effettuare join:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name\n  , properties -&gt;&gt; 'color'\n  , properties -&gt;&gt; 'country'\nFROM entity_jsonb \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nCon EAV ti serviranno 2 unioni per ogni propriet\u00e0 che desideri interrogare. A mio avviso, le query sopra riportate mostrano una notevole semplificazione nel design del database. Puoi trovare ulteriori esempi su come scrivere query per JSONB anche nel <noindex><a rel=\"nofollow\" href=\"http:\/\/schinckel.net\/2014\/05\/25\/querying-json-in-postgres\/\">questo<\/a><\/noindex> post.<br \/>\n\u00c8 ora di parlare delle prestazioni.<\/p>\n<h3>Prestazioni<\/h3>\n<p>\nPer confrontare le prestazioni, ho utilizzato <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/sql-explain.html\">EXPLAIN ANALYZE<\/a><\/noindex> negli interrogativi, per misurare il tempo di esecuzione. Ogni query \u00e8 stata eseguita almeno tre volte, poich\u00e9 alla prima il pianificatore di query richiede pi\u00f9 tempo. Innanzitutto ho eseguito le query senza alcun indice. Ovviamente, questo ha avvantaggiato JSONB, poich\u00e9 le unioni necessarie per EAV non potevano utilizzare indici (i campi delle chiavi esterne non erano indicizzati). Successivamente, ho creato un indice per 2 colonne delle chiavi esterne nella tabella dei valori EAV, e un indice <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/textsearch-indexes.html\">GIN<\/a><\/noindex> per la colonna JSONB.<\/p>\n<p>Gli aggiornamenti dei dati hanno mostrato i seguenti risultati in termini di tempo (in ms). Si noti che la scala \u00e8 logaritmica:<\/p>\n<p><img decoding=\"async\" alt=\"Sostituzione di EAV con JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/8a12ccd7a46d04868b1cc5fcaf01a581.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVediamo che JSONB \u00e8 molto pi\u00f9 veloce (&gt; 50000 volte) rispetto a EAV, se non si utilizzano gli indici, per il motivo sopra indicato. Quando indicizziamo le colonne con le chiavi primarie, la differenza si riduce quasi a zero, ma JSONB \u00e8 comunque 1,3 volte pi\u00f9 veloce di EAV. Si noti che l'indice nella colonna JSONB non ha alcun effetto qui, poich\u00e9 non stiamo utilizzando la colonna delle propriet\u00e0 nei criteri di valutazione. <\/p>\n<p>Per la selezione dei dati basata sul valore delle propriet\u00e0 otteniamo i seguenti risultati (scala normale):<\/p>\n<p><img decoding=\"async\" alt=\"Sostituzione di EAV con JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/e9a1fd797ef60132e661da686f6214a3.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nSi pu\u00f2 notare che JSONB funziona di nuovo pi\u00f9 velocemente di EAV senza indici, ma quando EAV ha indici, risulta comunque pi\u00f9 veloce di JSONB. Tuttavia, ho notato che i tempi delle query JSONB erano identici, il che mi ha portato a capire che gli indici GIN non venivano attivati. Evidentemente, quando si utilizza un indice GIN per una colonna con propriet\u00e0 popolate, esso funziona solo con l'operatore di inclusione @&gt;. Ho utilizzato questo in un nuovo test, il che ha avuto un enorme impatto sui tempi: solo 0,153 ms! Questo \u00e8 15000 volte pi\u00f9 veloce di EAV e 25000 volte pi\u00f9 veloce dell'operatore -&gt;&gt;. <\/p>\n<p>Penso che sia stato abbastanza veloce!<\/p>\n<h3>Dimensione delle tabelle del database<\/h3>\n<p>\nConfrontiamo le dimensioni delle tabelle utilizzando entrambi gli approcci. In psql possiamo mostrare la dimensione di tutte le tabelle e degli indici con il comando <b>dti+<\/b><\/p>\n<p><img decoding=\"async\" alt=\"Sostituzione di EAV con JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/c6c1fe7901fe9ae2bd446cf4ae99b923.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nPer l'approccio EAV, le dimensioni delle tabelle sono di circa 3068 MB, mentre gli indici arrivano fino a 3427 MB, per un totale di 6,43 GB. Utilizzando l'approccio JSONB, si utilizzano 1817 MB per la tabella e 318 MB per gli indici, per un totale di 2,08 GB. Risultano quasi tre volte inferiori! Questo dato mi ha sorpreso un po', perch\u00e9 memorizziamo i nomi delle propriet\u00e0 in ogni oggetto JSONB. <\/p>\n<p>Tuttavia, i numeri parlano da soli: nell'EAV memorizziamo 2 chiavi esterne intere per il valore dell'attributo, con un costo aggiuntivo di 8 byte. Inoltre, in EAV tutti i valori delle propriet\u00e0 sono memorizzati come testo, mentre JSONB utilizzer\u00e0 valori numerici e logici quando possibile, risultando in un volume complessivo inferiore.<\/p>\n<h3>Risultati<\/h3>\n<p>\nIn generale, credo che mantenere le propriet\u00e0 delle entit\u00e0 nel formato JSONB possa semplificare notevolmente la progettazione e la gestione del database. Se esegui molte query, tutto ci\u00f2 che \u00e8 memorizzato in una singola tabella con l'entit\u00e0 funzioner\u00e0 davvero in modo pi\u00f9 efficiente. E il fatto che questo semplifichi le interazioni tra i dati \u00e8 gi\u00e0 un vantaggio, ma anche il risultato finale del database \u00e8 tre volte pi\u00f9 piccolo.<\/p>\n<p>Inoltre, dai test effettuati, si pu\u00f2 concludere che le perdite di performance sono davvero trascurabili. In alcuni casi, JSONB addirittura funziona pi\u00f9 velocemente rispetto a EAV, il che lo rende ancora migliore. Tuttavia, questo test di riferimento, ovviamente, non copre tutti gli aspetti (ad esempio, entit\u00e0 con un numero molto elevato di propriet\u00e0, un aumento significativo del numero di propriet\u00e0 dei dati esistenti,...), quindi, se hai suggerimenti su come migliorarlo, non esitare a lasciare commenti!<br \/>\n<br \/>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/post\/475178\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0411\u0414 (\u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445): \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b) \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438. \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0432 [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-52584","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 4.9.10 - aioseo.com -->\n\t<meta name=\"description\" content=\"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0411\u0414 (\u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445): \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b) \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438. \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0432\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 4.9.10\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0417\u0430\u043c\u0435\u043d\u0430 EAV \u043d\u0430 JSONB \u0432 PostgreSQL | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0411\u0414 (\u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445): \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b) \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438. \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0432\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-11-11T21:00:00+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2020-02-18T11:00:21+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Sostituzione di EAV con JSONB in PostgreSQL | ProHoster","description":"TL; DR: JSONB pu\u00f2 semplificare notevolmente lo sviluppo dello schema del database senza compromettere le prestazioni delle query. Introduzione Presentiamo un classico esempio, probabilmente uno dei pi\u00f9 antichi utilizzi nel mondo dei database relazionali (DB): abbiamo un'entit\u00e0 e dobbiamo memorizzare determinati attributi di questa entit\u00e0. Tuttavia, non tutti gli esemplari possono avere lo stesso insieme di propriet\u00e0, inoltre nel","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0417\u0430\u043c\u0435\u043d\u0430 EAV \u043d\u0430 JSONB \u0432 PostgreSQL | ProHoster","og:description":"TL; DR: JSONB \u043c\u043e\u0436\u0435\u0442 \u0437\u043d\u0430\u0447\u0438\u0442\u0435\u043b\u044c\u043d\u043e \u0443\u043f\u0440\u043e\u0441\u0442\u0438\u0442\u044c \u0440\u0430\u0437\u0440\u0430\u0431\u043e\u0442\u043a\u0443 \u0441\u0445\u0435\u043c\u044b \u0411\u0414 \u0431\u0435\u0437 \u0443\u0449\u0435\u0440\u0431\u0430 \u043f\u0440\u043e\u0438\u0437\u0432\u043e\u0434\u0438\u0442\u0435\u043b\u044c\u043d\u043e\u0441\u0442\u0438 \u0432 \u0437\u0430\u043f\u0440\u043e\u0441\u0430\u0445. \u0412\u0432\u0435\u0434\u0435\u043d\u0438\u0435 \u041f\u0440\u0438\u0432\u0435\u0434\u0435\u043c \u043a\u043b\u0430\u0441\u0441\u0438\u0447\u0435\u0441\u043a\u0438\u0439 \u043f\u0440\u0438\u043c\u0435\u0440, \u043d\u0430\u0432\u0435\u0440\u043d\u043e\u0435, \u043e\u0434\u043d\u043e\u0433\u043e \u0438\u0437 \u0441\u0442\u0430\u0440\u0435\u0439\u0448\u0438\u0445 \u0432\u0430\u0440\u0438\u0430\u043d\u0442\u043e\u0432 \u0438\u0441\u043f\u043e\u043b\u044c\u0437\u043e\u0432\u0430\u043d\u0438\u044f \u0432 \u043c\u0438\u0440\u0435 \u0440\u0435\u043b\u044f\u0446\u0438\u043e\u043d\u043d\u044b\u0445 \u0411\u0414 (\u0431\u0430\u0437\u0430 \u0434\u0430\u043d\u043d\u044b\u0445): \u0443 \u043d\u0430\u0441 \u0435\u0441\u0442\u044c \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u044c, \u0438 \u043d\u0435\u043e\u0431\u0445\u043e\u0434\u0438\u043c\u043e \u0441\u043e\u0445\u0440\u0430\u043d\u0438\u0442\u044c \u043e\u043f\u0440\u0435\u0434\u0435\u043b\u0435\u043d\u043d\u044b\u0435 \u0441\u0432\u043e\u0439\u0441\u0442\u0432\u0430 (\u0430\u0442\u0440\u0438\u0431\u0443\u0442\u044b) \u044d\u0442\u043e\u0439 \u0441\u0443\u0449\u043d\u043e\u0441\u0442\u0438. \u041d\u043e \u043d\u0435 \u0432\u0441\u0435 \u044d\u043a\u0437\u0435\u043c\u043f\u043b\u044f\u0440\u044b \u043c\u043e\u0433\u0443\u0442 \u0438\u043c\u0435\u044e\u0442 \u043e\u0434\u0438\u043d\u0430\u043a\u043e\u0432\u044b\u0439 \u043d\u0430\u0431\u043e\u0440 \u0441\u0432\u043e\u0439\u0441\u0442\u0432, \u043a \u0442\u043e\u043c\u0443 \u0436\u0435 \u0432","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-11-11T21:00:00+00:00","article:modified_time":"2020-02-18T11:00:21+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"52584","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-24 04:07:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-02-28 20:40:27","updated":"2026-01-24 04:07:19"},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/52584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=52584"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/52584\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=52584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=52584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=52584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}