{"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\/pl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","title":{"rendered":"Zast\u0105pienie EAV przez JSONB w PostgreSQL","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<blockquote><p>TL; DR: JSONB mo\u017ce znacz\u0105co upro\u015bci\u0107 rozw\u00f3j schematu bazy danych bez uszczerbku dla wydajno\u015bci zapyta\u0144.<\/p><\/blockquote>\n<p><\/p>\n<h3>Wprowadzenie<\/h3>\n<p>\nPodamy klasyczny przyk\u0142ad, prawdopodobnie jednego z najstarszych zastosowa\u0144 w \u015bwiecie relacyjnych baz danych: mamy encj\u0119 i musimy zachowa\u0107 pewne w\u0142a\u015bciwo\u015bci (atrybuty) tej encji. Jednak nie wszystkie instancje mog\u0105 mie\u0107 t\u0119 sam\u0105 zestaw w\u0142a\u015bciwo\u015bci, a ponadto w przysz\u0142o\u015bci mo\u017cna doda\u0107 dodatkowe w\u0142a\u015bciwo\u015bci.<\/p>\n<p>Najprostszym sposobem rozwi\u0105zania tego problemu jest utworzenie kolumny w tabeli bazy danych dla ka\u017cdego mo\u017cliwego atrybutu i po prostu wype\u0142nianie tych, kt\u00f3re s\u0105 potrzebne dla danej instancji encji. \u015awietnie! Problem rozwi\u0105zany\u2026 dop\u00f3ki twoja tabela nie zawiera milion\u00f3w rekord\u00f3w i nie zajdzie potrzeba dodania nowego wpisu.<\/p>\n<p>Rozwa\u017cmy wzorzec EAV (<noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Entity%E2%80%93attribute%E2%80%93value_model\">Entity-Attribute-Value<\/a><\/noindex>), wyst\u0119puje on do\u015b\u0107 cz\u0119sto. Jedna tabela zawiera encje (rekordy), inna tabela zawiera nazwy w\u0142a\u015bciwo\u015bci (atrybut\u00f3w), a trzecia tabela \u0142\u0105czy encje z ich atrybutami i zawiera warto\u015bci tych atrybut\u00f3w dla bie\u017c\u0105cej encji. Daje to mo\u017cliwo\u015b\u0107 posiadania r\u00f3\u017cnych zestaw\u00f3w w\u0142a\u015bciwo\u015bci dla r\u00f3\u017cnych obiekt\u00f3w, a tak\u017ce dodawania atrybut\u00f3w \u201ew locie\u201d, bez zmiany struktury bazy danych.<br \/>\n<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><br \/>\nMimo to, nie pisa\u0142bym tej uwagi, gdyby nie by\u0142o wad w podej\u015bciu z u\u017cyciem EAV. Na przyk\u0142ad, aby uzyska\u0107 jedn\u0105 lub kilka encji, kt\u00f3re maj\u0105 jeden atrybut, potrzebne s\u0105 2 z\u0142\u0105czenia w zapytaniu: pierwsze \u2013 z\u0142\u0105czenie z tabel\u0105 atrybut\u00f3w, drugie \u2013 z\u0142\u0105czenie z tabel\u0105 warto\u015bci. Je\u015bli encja ma 2 atrybuty, potrzebujesz ju\u017c 4 z\u0142\u0105cze\u0144! Ponadto wszystkie atrybuty zazwyczaj s\u0105 przechowywane jako ci\u0105gi, co prowadzi do konwersji typ\u00f3w, zar\u00f3wno dla wynik\u00f3w, jak i dla warunku WHERE. Je\u015bli piszesz wiele zapyta\u0144, staje si\u0119 to do\u015b\u0107 kosztowne pod wzgl\u0119dem u\u017cycia zasob\u00f3w.<\/p>\n<p>Pomimo tych oczywistych wad, EAV jest od dawna wykorzystywane do rozwi\u0105zywania tego rodzaju problem\u00f3w. To by\u0142y nieuniknione braki, a nie by\u0142o lepszej alternatywy. <br \/>\nAle w\u00f3wczas w PostgreSQL pojawi\u0142a si\u0119 nowa \u201etechnologia\u201d\u2026<\/p>\n<p>Od wersji PostgreSQL 9.4 dodano typ danych JSONB do przechowywania danych binarnych JSON. Chocia\u017c przechowywanie JSON w tym formacie zazwyczaj zajmuje nieco wi\u0119cej miejsca i czasu ni\u017c prosty tekst JSON, operacje na nim s\u0105 znacznie szybsze. JSONB obs\u0142uguje r\u00f3wnie\u017c indeksowanie, co przyspiesza zapytania.<\/p>\n<p>Typ danych JSONB umo\u017cliwia zast\u0105pienie niepor\u0119cznego wzorca EAV poprzez dodanie tylko jednej kolumny JSONB do naszej tabeli encji, co znacz\u0105co upraszcza projektowanie bazy danych. Jednak wielu twierdzi, \u017ce powinno to wi\u0105za\u0107 si\u0119 z obni\u017ceniem wydajno\u015bci\u2026 Z tego powodu powsta\u0142 ten artyku\u0142.<\/p>\n<h3>Konfiguracja testowej bazy danych<\/h3>\n<p>\nDo tego por\u00f3wnania stworzy\u0142em baz\u0119 danych na nowej instalacji PostgreSQL 9.5 na 80-dolarowej konfiguracji <noindex><a rel=\"nofollow\" href=\"https:\/\/www.digitalocean.com\/\">DigitalOcean<\/a><\/noindex> Ubuntu 14.04. Po skonfigurowaniu niekt\u00f3rych parametr\u00f3w w postgresql.conf uruchomi\u0142em <noindex><a rel=\"nofollow\" href=\"https:\/\/gist.github.com\/coussej\/80c385332ce37df6687f\">ten<\/a><\/noindex> skrypt za pomoc\u0105 psql. Dla przedstawienia danych w formacie EAV utworzono nast\u0119puj\u0105ce tabele:<\/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>\nPoni\u017cej znajduje si\u0119 tabela, w kt\u00f3rej b\u0119d\u0105 przechowywane te same dane, ale z atrybutami w kolumnie typu 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>\nWygl\u0105da znacznie pro\u015bciej, prawda? Nast\u0119pnie dodano do tabel encji (<i>entity<\/i> &amp; <i>entity_jsonb<\/i>) 10 milion\u00f3w rekord\u00f3w, a odpowiednio wype\u0142niono identycznymi danymi tabele, w kt\u00f3rych stosuje si\u0119 wzorzec EAV oraz podej\u015bcie z kolumn\u0105 JSONB \u2013 <i>entity_jsonb.properties<\/i>. W ten spos\u00f3b otrzymali\u015bmy kilka r\u00f3\u017cnych typ\u00f3w danych w ca\u0142ym zbiorze w\u0142a\u015bciwo\u015bci. Przyk\u0142ad danych:<\/p>\n<pre><code class=\"json\">{\n  id:          1\n  name:        \"Entity1\"\n  description: \"Testowa encja nr 1\"\n  properties:  {\n    color:        \"czerwony\"\n    length:       120\n    width:        3.1882420\n    hasSomething: true\n    country:      \"Belgia\"\n  } \n}<\/code><\/pre>\n<p>\nWi\u0119c teraz mamy identyczne dane dla dw\u00f3ch opcji. Zaczynajmy por\u00f3wnywa\u0107 implementacje w praktyce!<\/p>\n<h3>Uproszczenie projektu<\/h3>\n<p>\nJu\u017c wcze\u015bniej wspomniano, \u017ce projekt bazy danych zosta\u0142 znacznie uproszczony: jedna tabela, dzi\u0119ki wykorzystaniu kolumny JSONB dla w\u0142a\u015bciwo\u015bci, zamiast stosowania trzech tabel dla EAV. Ale jak to wp\u0142ywa na zapytania? Aktualizacja jednego atrybutu encji wygl\u0105da nast\u0119puj\u0105co:<\/p>\n<pre><code class=\"pgsql\">-- EAV\nUPDATE entity_attribute_value \nSET value = 'niebieski' \nWHERE entity_attribute_id = 1 \n  AND entity_id = 120;\n\n-- JSONB\nUPDATE entity_jsonb \nSET properties = jsonb_set(properties, '{\"color\"}', '\"niebieski\"') \nWHERE id = 120;\n<\/code><\/pre>\n<p>\nJak widzimy, ostatnie zapytanie nie wydaje si\u0119 prostsze. Aby zaktualizowa\u0107 warto\u015b\u0107 w\u0142a\u015bciwo\u015bci w obiekcie JSONB, musimy u\u017cy\u0107 funkcji <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.5\/static\/functions-json.html\">jsonb_set()<\/a><\/noindex>, i musimy przekaza\u0107 nasz\u0105 now\u0105 warto\u015b\u0107 jako obiekt JSONB. Niemniej jednak, nie musimy zna\u0107 \u017cadnego identyfikatora z g\u00f3ry. Patrz\u0105c na przyk\u0142ad z EAV, musimy zna\u0107 zar\u00f3wno entity_id, jak i entity_attribute_id, aby wykona\u0107 aktualizacj\u0119. Je\u015bli chcesz zaktualizowa\u0107 w\u0142a\u015bciwo\u015b\u0107 w kolumnie JSONB na podstawie nazwy obiektu, \u2013 wszystko to mo\u017cna zrobi\u0107 w jednym prostym wierszu.<\/p>\n<p>Teraz wybierzmy t\u0119 encj\u0119, kt\u00f3r\u0105 w\u0142a\u015bnie zaktualizowali\u015bmy, na podstawie jej nowego koloru:<\/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 = 'niebieski';\n\n-- JSONB\nSELECT name \nFROM entity_jsonb \nWHERE properties -&gt;&gt; 'color' = 'niebieski';\n<\/code><\/pre>\n<p>\nMy\u015bl\u0119, \u017ce mo\u017cemy zgodzi\u0107 si\u0119, \u017ce drugie jest kr\u00f3tsze (bez join!), a tym samym bardziej czytelne. Tutaj wygrywa JSONB! U\u017cywamy operatora JSON -&gt;&gt;, aby uzyska\u0107 kolor jako warto\u015b\u0107 tekstow\u0105 z obiektu JSONB. Istnieje r\u00f3wnie\u017c drugi spos\u00f3b osi\u0105gni\u0119cia tego samego wyniku w modelu JSONB z u\u017cyciem operatora @&gt;:<\/p>\n<pre><code class=\"pgsql\">-- JSONB \nSELECT name \nFROM entity_jsonb \nWHERE properties @&gt; '{\"color\": \"niebieski\"}';\n<\/code><\/pre>\n<p>\nTo jest troch\u0119 trudniejsze: sprawdzamy, czy obiekt JSON w kolumnie w\u0142a\u015bciwo\u015bci zawiera obiekt, kt\u00f3ry znajduje si\u0119 po prawej stronie operatora @&gt;. Mniej czytelny, bardziej wydajny (patrz dalej). <\/p>\n<p>Upro\u015b\u0107my korzystanie z JSONB jeszcze bardziej, gdy potrzebujesz wybra\u0107 kilka w\u0142a\u015bciwo\u015bci jednocze\u015bnie. To jest miejsce, gdzie podej\u015bcie JSONB naprawd\u0119 si\u0119 sprawdza: po prostu wybieramy w\u0142a\u015bciwo\u015bci jako dodatkowe kolumny w naszym zbiorze wynik\u00f3w bez potrzeby unii:<\/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>\nZ EAV potrzebujesz 2 unii dla ka\u017cdej w\u0142a\u015bciwo\u015bci, kt\u00f3r\u0105 chcesz zapyta\u0107. Moim zdaniem powy\u017csze zapytania pokazuj\u0105 du\u017ce uproszczenie w projektowaniu bazy danych. Mo\u017cesz r\u00f3wnie\u017c znale\u017a\u0107 wi\u0119cej przyk\u0142ad\u00f3w pisania zapyta\u0144 do JSONB w <noindex><a rel=\"nofollow\" href=\"http:\/\/schinckel.net\/2014\/05\/25\/querying-json-in-postgres\/\">tym<\/a><\/noindex> po\u015bcie.<br \/>\nTeraz nadszed\u0142 czas, aby porozmawia\u0107 o wydajno\u015bci.<\/p>\n<h3>Wydajno\u015b\u0107<\/h3>\n<p>\nAby por\u00f3wna\u0107 wydajno\u015b\u0107, u\u017cy\u0142em <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/sql-explain.html\">EXPLAIN ANALYZE<\/a><\/noindex> w zapytaniach, do obliczania czasu realizacji. Ka\u017cde zapytanie by\u0142o wykonywane co najmniej trzy razy, poniewa\u017c za pierwszym razem planowanie zapyta\u0144 wymaga wi\u0119cej czasu. Najpierw wykona\u0142em zapytania bez jakichkolwiek indeks\u00f3w. Oczywi\u015bcie, mia\u0142o to zalet\u0119 dla JSONB, poniewa\u017c po\u0142\u0105czenia wymagane dla EAV nie mog\u0142y wykorzystywa\u0107 indeks\u00f3w (pola kluczy obcych nie by\u0142y indeksowane). Nast\u0119pnie stworzy\u0142em indeks dla dw\u00f3ch kolumn kluczy obcych w tabeli warto\u015bci EAV oraz indeks <noindex><a rel=\"nofollow\" href=\"http:\/\/www.postgresql.org\/docs\/9.1\/static\/textsearch-indexes.html\">GIN<\/a><\/noindex> dla kolumny JSONB.<\/p>\n<p>Aktualizacja danych pokaza\u0142a nast\u0119puj\u0105ce wyniki czasowe (w ms). Zwr\u00f3\u0107 uwag\u0119, \u017ce skala jest logarytmiczna:<\/p>\n<p><img decoding=\"async\" alt=\"Zast\u0105pienie EAV przez JSONB w PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/8a12ccd7a46d04868b1cc5fcaf01a581.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nWidzimy, \u017ce JSONB jest znacznie (&gt; 50000-krotnie) szybszy ni\u017c EAV, je\u015bli nie u\u017cywamy indeks\u00f3w, z powodu podanych wcze\u015bniej przyczyn. Gdy indeksujemy kolumny z kluczami g\u0142\u00f3wnymi, r\u00f3\u017cnica niemal znika, ale JSONB wci\u0105\u017c jest 1,3 razy szybszy ni\u017c EAV. Zauwa\u017c, \u017ce indeks w kolumnie JSONB nie wp\u0142ywa tutaj w \u017caden spos\u00f3b, poniewa\u017c nie u\u017cywamy kolumny w\u0142a\u015bciwo\u015bci w kryteriach oceny. <\/p>\n<p>Do wyboru danych na podstawie warto\u015bci w\u0142a\u015bciwo\u015bci uzyskujemy nast\u0119puj\u0105ce wyniki (zwyk\u0142a skala):<\/p>\n<p><img decoding=\"async\" alt=\"Zast\u0105pienie EAV przez JSONB w PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/e9a1fd797ef60132e661da686f6214a3.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nMo\u017cna zauwa\u017cy\u0107, \u017ce JSONB zn\u00f3w dzia\u0142a szybciej ni\u017c EAV bez indeks\u00f3w, ale kiedy EAV ma indeksy \u2013 nadal dzia\u0142a szybciej ni\u017c JSONB. Jednak potem zauwa\u017cy\u0142em, \u017ce czas dla zapyta\u0144 JSONB by\u0142 jednakowy, co sk\u0142oni\u0142o mnie do my\u015blenia, \u017ce indeks GIN nie dzia\u0142a. Wygl\u0105da na to, \u017ce gdy u\u017cywasz indeksu GIN dla kolumny z wype\u0142nionymi w\u0142a\u015bciwo\u015bciami, dzia\u0142a on tylko przy u\u017cyciu operatora w\u0142\u0105czenia @&gt;. U\u017cy\u0142em tego w nowym te\u015bcie, co mia\u0142o ogromny wp\u0142yw na czas: zaledwie 0,153 ms! To 15000 razy szybciej ni\u017c EAV i 25000 razy szybciej ni\u017c operator -&gt;&gt;. <\/p>\n<p>My\u015bl\u0119, \u017ce to by\u0142o wystarczaj\u0105co szybko!<\/p>\n<h3>Rozmiary tabel bazy danych<\/h3>\n<p>\nPor\u00f3wnajmy rozmiary tabel w obu podej\u015bciach. W psql mo\u017cemy pokaza\u0107 rozmiar wszystkich tabel i indeks\u00f3w za pomoc\u0105 polecenia <b>dti+<\/b><\/p>\n<p><img decoding=\"async\" alt=\"Zast\u0105pienie EAV przez JSONB w PostgreSQL\" src=\"\/wp-content\/uploads\/2019\/11\/c6c1fe7901fe9ae2bd446cf4ae99b923.jpg\" style=\"display:block;margin: 0 auto;\" \/><br \/>\n<br \/>\nDla podej\u015bcia EAV rozmiary tabel wynosz\u0105 oko\u0142o 3068 MB, a indeksy do 3427 MB, co w sumie daje 6,43 GB. Przy u\u017cyciu podej\u015bcia z JSONB u\u017cywa si\u0119 1817 MB dla tabeli i 318 MB dla indeks\u00f3w, co stanowi 2,08 GB. To trzykrotnie mniej! Ten fakt troch\u0119 mnie zaskoczy\u0142, poniewa\u017c przechowujemy nazwy w\u0142a\u015bciwo\u015bci w ka\u017cdym obiekcie JSONB. <\/p>\n<p>Jednak liczby m\u00f3wi\u0105 same za siebie: w EAV przechowujemy 2 klucze zewn\u0119trzne jako liczby ca\u0142kowite dla warto\u015bci atrybut\u00f3w, co skutkuje dodatkowymi 8 bajtami danych. Co wi\u0119cej, w EAV wszystkie warto\u015bci w\u0142a\u015bciwo\u015bci s\u0105 przechowywane jako tekst, podczas gdy JSONB wykorzystuje warto\u015bci liczbowe i logiczne tam, gdzie to mo\u017cliwe, co prowadzi do mniejszej obj\u0119to\u015bci.<\/p>\n<h3>Podsumowanie<\/h3>\n<p>\nOg\u00f3lnie s\u0105dz\u0119, \u017ce zachowanie w\u0142a\u015bciwo\u015bci encji w formacie JSONB mo\u017ce znacznie upro\u015bci\u0107 projektowanie i utrzymanie bazy danych. Je\u015bli wykonujesz wiele zapyta\u0144, wszystko, co jest przechowywane w jednej tabeli z encj\u0105, b\u0119dzie dzia\u0142a\u0107 efektywniej. I fakt, \u017ce to upraszcza interakcj\u0119 mi\u0119dzy danymi, ju\u017c jest plusem, ale tak\u017ce prowadzi do mniejszej obj\u0119to\u015bci bazy danych, kt\u00f3ra jest trzykrotnie mniejsza.<\/p>\n<p>Ponadto z przeprowadzonych test\u00f3w mo\u017cna wyci\u0105gn\u0105\u0107 wnioski, \u017ce straty wydajno\u015bci s\u0105 bardzo nieznaczne. W niekt\u00f3rych przypadkach JSONB dzia\u0142a nawet szybciej ni\u017c EAV, co czyni go jeszcze lepszym. Jednak ten test wzorcowy oczywi\u015bcie nie obejmuje wszystkich aspekt\u00f3w (np. encje z bardzo du\u017c\u0105 liczb\u0105 w\u0142a\u015bciwo\u015bci, znacznym wzrostem liczby istniej\u0105cych danych,\u2026), dlatego je\u015bli masz jakiekolwiek sugestie, jak je poprawi\u0107, nie wahaj si\u0119 zostawi\u0107 komentarza!<br \/>\n<br \/>\u0179r\u00f3d\u0142o: <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.1.1 - 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\/pl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.1.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"pl_PL\" \/>\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\/pl\/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\udd47Zamiana EAV na JSONB w PostgreSQL | ProHoster","description":"TL; DR: JSONB mo\u017ce znacz\u0105co upro\u015bci\u0107 rozw\u00f3j schematu bazy danych bez utraty wydajno\u015bci w zapytaniach. Wprowadzenie Przedstawimy klasyczny przyk\u0142ad, prawdopodobnie jednego z najstarszych.","canonical_url":"https:\/\/prohoster.info\/pl\/blog\/administrirovanie\/zamena-eav-na-jsonb-v-postgresql","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"pl_PL","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\/pl\/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\/pl\/wp-json\/wp\/v2\/posts\/52584","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/comments?post=52584"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/posts\/52584\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/media?parent=52584"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/categories?post=52584"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/pl\/wp-json\/wp\/v2\/tags?post=52584"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}