{"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\/nl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","title":{"rendered":"Vervanging van EAV door JSONB in PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<blockquote><p>TL; DR: JSONB kan de ontwikkeling van een database-schema aanzienlijk vereenvoudigen zonder in te boeten op de prestaties van zoekopdrachten.<\/p><\/blockquote>\n<p><\/p>\n<h3>Inleiding<\/h3>\n<p>\nLaten we een klassiek voorbeeld bekijken, waarschijnlijk een van de oudste toepassingen in de wereld van relationele databases: we hebben een entiteit en moeten bepaalde kenmerken (attributen) van deze entiteit opslaan. Maar niet alle instanties kunnen dezelfde set kenmerken hebben, en bovendien kunnen in de toekomst mogelijk nog meer kenmerken worden toegevoegd.<\/p>\n<p>De eenvoudigste manier om dit probleem op te lossen, is door een kolom in de database-tabel te maken voor elke waarde van een kenmerk, en gewoon diegene in te vullen die nodig zijn voor een bepaalde instantie van de entiteit. Prima! Probleem opgelost... tot het moment dat uw tabel miljoenen records bevat en u de noodzaak heeft om een nieuw record toe te voegen.<\/p>\n<p>Laten we het EAV-patroon overwegen (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">Entity-Attribute-Value<\/a><\/noindex>), dat komt vrij vaak voor. Een tabel bevat entiteiten (records), een andere tabel bevat de namen van kenmerken (attributen), en een derde tabel verbindt de entiteiten met hun attributen en bevat de waarde van deze attributen voor de huidige entiteit. Dit geeft u de mogelijkheid om verschillende sets kenmerken voor verschillende objecten te hebben, en ook om eigenschappen 'ter plekke' toe te voegen zonder de database-structuur te wijzigen.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nToch zou ik deze opmerking niet maken als er geen nadelen aan de EVA-aanpak kleven. Bijvoorbeeld, om \u00e9\u00e9n of meerdere entiteiten te krijgen die elk 1 attribuut hebben, zijn er 2 joins vereist in de query: de eerste is een join met de attribuuttabel, de tweede is een join met de waardetabel. Als de entiteit 2 attributen heeft, zijn er al 4 joins nodig! Bovendien worden alle attributen meestal als strings opgeslagen, wat leidt tot typeconversies, zowel voor het resultaat als voor de WHERE-clausule. Als je veel queries schrijft, is dit vrij ineffici\u00ebnt qua resourcegebruik.<\/p>\n<p>Ondanks deze voor de hand liggende nadelen wordt EAV al lang gebruikt om dit soort problemen op te lossen. Dit zijn onvermijdelijke nadelen, en er was simpelweg geen betere oplossing. <br \/>\nMaar toen kwam er een nieuwe 'technologie' in PostgreSQL...<\/p>\n<p>Met PostgreSQL 9.4 is het datatype JSONB toegevoegd voor het opslaan van binaire JSON-gegevens. Hoewel het opslaan van JSON in dit formaat doorgaans iets meer ruimte en tijd kost dan gewone tekst-JSON, zijn de bewerkingen ermee veel sneller. Bovendien ondersteunt JSONB indexering, wat de aanvragen nog sneller maakt.<\/p>\n<p>Het datatype JSONB stelt ons in staat om het onhandige EAV-patroon te vervangen door slechts \u00e9\u00e9n JSONB-kolom aan onze entiteitentabel toe te voegen, wat het databaseontwerp aanzienlijk vereenvoudigt. Maar velen beweren dat dit gepaard moet gaan met een vermindering van de prestaties... Om deze reden is dit artikel ontstaan.<\/p>\n<h3>Configuratie van de testdatabase<\/h3>\n<p>\nVoor deze vergelijking heb ik een database gemaakt op een nieuwe installatie van PostgreSQL 9.5 op een 80-dollarconfiguratie <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex> Ubuntu 14.04. Na het configureren van enkele instellingen in postgresql.conf heb ik <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/coussej\/80c385332ce37df6687f\">deze<\/a><\/noindex> script uitgevoerd met psql. Voor de weergave van de gegevens in EAV-vorm zijn de volgende tabellen aangemaakt:<\/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>\nHieronder is de tabel waarin dezelfde gegevens zullen worden opgeslagen, maar met de attributen in een JSONB-kolom \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>\nHet ziet er veel eenvoudiger uit, nietwaar? Vervolgens werden er 10 miljoen records toegevoegd aan de entiteitstabellen (<i>entity<\/i> &amp; <i>entity_jsonb<\/i>) en de tabellen die het EAV-patroon gebruiken en de benadering met de JSONB-kolom werden overeenkomstig gevuld met identieke gegevens \u2013 <i>entity_jsonb.properties<\/i>. Op deze manier hebben we verschillende datatypes verkregen onder het totale eigenschappenpakket. Voorbeeldgegevens:<\/p>\n<pre><code class=\"json\">{\n  id:          1\n  name:        \"Entity1\"\n  description: \"Test entiteit nr. 1\"\n  properties:  {\n    color:        \"rood\"\n    lengte:      120\n    breedte:     3.1882420\n    heeftIets:  true\n    land:        \"Belgi\u00eb\"\n  } \n}<\/code><\/pre>\n<p>\nDus, nu hebben we identieke gegevens voor twee opties. Laten we beginnen met het vergelijken van de implementaties in de praktijk!<\/p>\n<h3>Vereenvoudigen van het ontwerp<\/h3>\n<p>\nEerder is al vermeld dat het databaseontwerp aanzienlijk is vereenvoudigd: \u00e9\u00e9n tabel, door het gebruik van een JSONB-kolom voor eigenschappen in plaats van het gebruik van drie tabellen voor EAV. Maar hoe weerspiegelt dit zich in de aanvragen? Het bijwerken van \u00e9\u00e9n eigenschap van de entiteit ziet er als volgt uit:<\/p>\n<pre><code class=\"pgsql\">-- EAV\nUPDATE entity_attribute_value \nSET value = 'blauw' \nWHERE entity_attribute_id = 1 \n  AND entity_id = 120;\n\n-- JSONB\nUPDATE entity_jsonb \nSET properties = jsonb_set(properties, '{\"color\"}', '\"blauw\"') \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nZoals we zien, lijkt de laatste query niet eenvoudiger. Om de waarde van een eigenschap in een JSONB-object bij te werken, moeten we de functie <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/functions-json.html\">jsonb_set()<\/a><\/noindex>, en we moeten onze nieuwe waarde doorgeven als een JSONB-object. We hoeven echter geen enkele identificator van tevoren te kennen. Kijken we naar het voorbeeld met EAV, moeten we zowel entity_id als entity_attribute_id kennen om de update uit te voeren. Als je een eigenschap in de JSONB-kolom wilt bijwerken op basis van de naam van het object, dan kan dat allemaal met \u00e9\u00e9n simpele regel.<\/p>\n<p>Laten we nu de entiteit selecteren die we zojuist hebben bijgewerkt, op basis van haar nieuwe kleur:<\/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 = 'kleur' AND eav.value = 'blauw';\n\n-- JSONB\nSELECT name \nFROM entity_jsonb \nWHERE properties -&gt;&gt; 'color' = 'blauw';\n<\/code><\/pre>\n<p>\nIk denk dat we het erover eens kunnen zijn dat de tweede korter is (zonder join!), en daarom leesbaarder. Hier wint JSONB! We gebruiken de JSON-operator -&gt;&gt;, om de kleur als tekstwaarde uit het JSONB-object te krijgen. Er is ook een tweede manier om hetzelfde resultaat te bereiken in het JSONB-model met de operator @&gt;:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name \nFROM entity_jsonb \nWHERE properties @&gt; '{\"color\": \"blauw\"}';\n<\/code><\/pre>\n<p>\nDit is iets ingewikkelder: we controleren of het JSON-object in de eigenschap kolom het object aan de rechterkant van de operator @&gt; bevat. Minder leesbaar, maar effici\u00ebnter (zie hieronder). <\/p>\n<p>Laten we het gebruik van JSONB nog verder vereenvoudigen, wanneer je meerdere eigenschappen tegelijk wilt selecteren. Hier komt de JSONB-aanpak echt van pas: we selecteren gewoon de eigenschappen als extra kolommen in onze resultaten zonder dat we joins nodig hebben:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name\n  , properties -&gt;&gt; 'kleur'\n  , properties -&gt;&gt; 'land'\nFROM entity_jsonb \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nMet EAV heb je 2 joins nodig voor elke eigenschap die je wilt opvragen. Naar mijn mening tonen de bovenstaande queries een grote vereenvoudiging in het databaseontwerp. Kijk ook naar meer voorbeelden van hoe je queries kunt schrijven voor JSONB, misschien ook in <noindex><a rel=\"nofollow\" href=\"http:\/\/schinckel.net\/2014\/05\/25\/querying-json-in-postgres\/\">deze<\/a><\/noindex> post.<br \/>\nHet is nu tijd om over prestaties te praten.<\/p>\n<h3>Prestaties<\/h3>\n<p>\nOm de prestaties te vergelijken, heb ik gebruikt <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/sql-explain.html\">EXPLAIN ANALYZE<\/a><\/noindex> in de aanvragen, voor het berekenen van de uitvoeringstijd. Elke aanvraag werd minimaal drie keer uitgevoerd, omdat de planner bij de eerste uitvoering meer tijd nodig heeft. Eerst heb ik de aanvragen zonder enige indices uitgevoerd. Dit was duidelijk een voordeel van JSONB, aangezien de joins die nodig zijn voor EAV geen gebruik konden maken van indices (de velden met vreemde sleutels werden niet ge\u00efndexeerd). Daarna heb ik een index gemaakt voor 2 kolommen van vreemde sleutels in de EAV-waarden tabel, evenals een index <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/textsearch-indexes.html\">GIN<\/a><\/noindex> voor de JSONB-kolom.<\/p>\n<p>De gegevensupdates toonden de volgende resultaten in tijd (in ms). Houd er rekening mee dat de schaal logaritmisch is:<\/p>\n<p><img decoding=\"async\" alt=\"Vervanging van EAV door JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/8a12ccd7a46d04868b1cc5fcaf01a581.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWe zien dat JSONB veel (&gt; 50000x) sneller is dan EAV, wanneer er geen indices worden gebruikt, om de eerder genoemde reden. Wanneer we de kolommen met primaire sleutels indexeren, verdwijnt het verschil bijna, maar JSONB is nog steeds 1,3 keer sneller dan EAV. Let op dat de index in de JSONB-kolom hier geen invloed heeft, omdat we de eigenschap kolom niet gebruiken in de beoordelingscriteria. <\/p>\n<p>Voor het selecteren van gegevens op basis van eigenschapswaarde krijgen we de volgende resultaten (normale schaal):<\/p>\n<p><img decoding=\"async\" alt=\"Vervanging van EAV door JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/e9a1fd797ef60132e661da686f6214a3.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWe kunnen opmerken dat JSONB opnieuw sneller werkt dan EAV zonder indices, maar wanneer EAV met indices is, werkt het nog steeds sneller dan JSONB. Maar toen merkte ik dat de tijd voor JSONB-aanvragen gelijk was, wat me deed beseffen dat de GIN-index niet werd geactiveerd. Schijnbaar geldt de GIN-index voor een kolom met ingevulde eigenschappen alleen wanneer de @&gt; operator wordt gebruikt. Ik gebruikte dit in een nieuwe test, wat een enorme impact had op de tijd: slechts 0,153 ms! Dat is 15000 keer sneller dan EAV en 25000 keer sneller dan de -&gt;&gt; operator. <\/p>\n<p>Ik denk dat het snel genoeg was!<\/p>\n<h3>Grootte van de databases<\/h3>\n<p>\nLaten we de groottes van de tabellen vergelijken bij beide benaderingen. In psql kunnen we de grootte van alle tabellen en indices laten zien met de opdracht <b>dti+<\/b><\/p>\n<p><img decoding=\"async\" alt=\"Vervanging van EAV door JSONB in PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/c6c1fe7901fe9ae2bd446cf4ae99b923.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nVoor de EAV-benadering bedragen de groottes van de tabellen ongeveer 3068 MB en de indices tot 3427 MB, wat totaal 6,43 GB oplevert. Bij het gebruik van de JSONB-benadering is het 1817 MB voor de tabel en 318 MB voor de indices, wat samen 2,08 GB is. Dat is 3 keer minder! Dit verraste me een beetje, omdat we de namen van de eigenschappen in elk JSONB-object opslaan. <\/p>\n<p>Maar cijfers spreken nu eenmaal voor zich: in EAV slaat men 2 integer externe sleutels op voor de waarde van het attribuut, wat resulteert in 8 extra bytes aan gegevens. Bovendien worden in EAV alle waarden van eigenschappen als tekst opgeslagen, terwijl JSONB numerieke en logische waarden gebruikt waar mogelijk, wat leidt tot een kleinere omvang.<\/p>\n<h3>Conclusies<\/h3>\n<p>\nOver het geheel genomen denk ik dat het opslaan van de eigenschappen van entiteiten in het JSONB-formaat het ontwerpen en onderhouden van uw database aanzienlijk kan vereenvoudigen. Als u veel zoekopdrachten uitvoert, zal alles wat in \u00e9\u00e9n tabel met de entiteit is opgeslagen, echt effici\u00ebnter werken. Het feit dat dit de interactie tussen gegevens vereenvoudigt, is al een pluspunt, maar ook de resulterende database is drie keer kleiner in omvang.<\/p>\n<p>Daarnaast kan uit de uitgevoerde tests worden geconcludeerd dat de prestatieverliezen zeer gering zijn. In sommige gevallen werkt JSONB zelfs sneller dan EAV, wat het nog beter maakt. Deze benchmarktest dekt natuurlijk niet alle aspecten (bijvoorbeeld entiteiten met een zeer groot aantal eigenschappen, een aanzienlijke toename van het aantal eigenschappen van bestaande gegevens,...), dus als u suggesties heeft voor verbetering, aarzel dan niet om deze in de opmerkingen achter te laten!<br \/>\n<br \/>Bron: <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 5.0.3 - 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.\" \/>\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\/nl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\n\t\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.3\" \/>\n\t\t<meta property=\"og:locale\" content=\"nl_NL\" \/>\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.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/nl\/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\udd47Vervanging van EAV door JSONB in PostgreSQL | ProHoster","description":"TL; DR: JSONB kan de ontwikkeling van een databaseschema aanzienlijk vereenvoudigen zonder in te boeten op prestatie tijdens zoekopdrachten. Inleiding Laten we een klassiek voorbeeld nemen, waarschijnlijk een van de oudst bekende.","canonical_url":"https:\/\/prohoster.info\/nl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"nl_NL","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.","og:url":"https:\/\/prohoster.info\/nl\/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","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/52584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/comments?post=52584"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/posts\/52584\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/media?parent=52584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/categories?post=52584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/nl\/wp-json\/wp\/v2\/tags?post=52584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}