Ngaganti EAV sareng JSONB dina PostgreSQL

TL; DR: JSONB tiasa pisan nyederhanakeun pangwangunan skéma database tanpa ngorbankeun kinerja query.

perkenalan

Hayu urang nyandak conto klasik, meureun salah sahiji kasus pamakéan pangkolotna di dunya database relational: urang boga hiji éntitas, sarta kami kudu nyimpen sipat nu tangtu (atribut) tina éntitas ieu. Tapi henteu sadayana instansi tiasa gaduh set sipat anu sami, sareng langkung seueur pasipatan tiasa ditambihkeun di hareup.

Solusi pangbasajanna pikeun masalah ieu nya éta nyieun kolom dina tabel database pikeun tiap nilai sipat tur saukur populate nu diperlukeun pikeun instansi entitas husus. Hebat! Masalahna direngsekeun ... dugi méja anjeun ngandung jutaan rékaman sareng anjeun kedah nambihan rékaman énggal.

Hayu urang pertimbangkeun pola EAV (Éntitas-Atribut-Nilai), éta cukup umum. Hiji tabel ngandung éntitas (rékaman), tabel séjén ngandung ngaran sipat (atribut), sarta tabel katilu numbu entitas ka atribut maranéhanana sarta ngandung nilai atribut ieu pikeun éntitas ayeuna. Hal ieu ngamungkinkeun anjeun gaduh sét sipat anu béda pikeun objék anu béda, ogé nambihan sipat dina laleur, tanpa ngarobih struktur database.

Nanging, kuring henteu bakal nyerat tulisan ieu upami teu aya sababaraha kalemahan dina pendekatan EVA. Contona, retrieving hiji atawa leuwih éntitas kalawan hiji atribut unggal merlukeun dua gabung dina query: kahiji gabung jeung tabel atribut, kadua gabung jeung tabel nilai. Lamun hiji éntitas boga dua atribut, lajeng opat gabung diperlukeun! Saterusna, sakabéh atribut ilaharna disimpen salaku string, nu ngakibatkeun tipe paksaan boh hasilna jeung klausa WHERE. Lamun nulis loba queries, ieu téh rada boros dina watesan pamakéan sumberdaya.

Sanajan kakurangan ieu atra, EAV geus lila dipaké pikeun ngajawab jenis ieu masalah. Ieu mangrupikeun kakurangan anu teu tiasa dihindari, sareng ngan saukur teu aya alternatif anu langkung saé.
Tapi teras "téhnologi" anyar muncul dina PostgreSQL…

Dimimitian ku PostgreSQL 9.4, tipe data JSONB ditambahkeun pikeun nyimpen data JSON binér. Sanaos nyimpen JSON dina format ieu biasana peryogi langkung seueur rohangan sareng waktos tibatan JSON téks biasa, operasi sareng éta langkung gancang. JSONB ogé ngarojong indexing, sahingga queries malah gancang.

Tipe data JSONB ngamungkinkeun urang pikeun ngaganti pola EAV pajeujeut ku nambahkeun ngan hiji kolom JSONB kana tabel éntitas urang, nyata nyederhanakeun desain database. Nanging, seueur anu ngabantah yén ieu kedah biaya dina pagelaran… Éta alesan kuring nyerat tulisan ieu.

Nyetél database test

Pikeun ngabandingkeun ieu, kuring nyiptakeun pangkalan data dina pamasangan anyar PostgreSQL 9.5 dina $ 80 ngawangun. DigitalOcean Ubuntu 14.04. После настройки некоторых параметров в postgresql.conf я запустил ieu skrip ngagunakeun psql. Pikeun nampilkeun data salaku EAV, tabél ieu didamel:

CREATE TABLE entity ( 
  id           SERIAL PRIMARY KEY, 
  name         TEXT, 
  description  TEXT
);
CREATE TABLE entity_attribute (
  id          SERIAL PRIMARY KEY, 
  name        TEXT
);
CREATE TABLE entity_attribute_value (
  id                  SERIAL PRIMARY KEY, 
  entity_id           INT    REFERENCES entity(id), 
  entity_attribute_id INT    REFERENCES entity_attribute(id), 
  value               TEXT
);

Di handap ieu tabel dimana data anu sami bakal disimpen, tapi kalayan atribut dina kolom JSONB - sipat.

CREATE TABLE entity_jsonb (
  id          SERIAL PRIMARY KEY, 
  name        TEXT, 
  description TEXT,
  properties  JSONB
);

Sigana leuwih basajan, teu eta? Lajeng ditambahkeun kana tabel éntitas (éntitas & entity_jsonb) 10 juta rékaman, sarta sasuai, tabél ieu ngeusi data idéntik dimana pola EAV jeung pendekatan jeung kolom JSONB dipaké - entity_jsonb.propertiesKu kituna, urang meunang sababaraha tipe data béda sakuliah sakabéh set sipat. conto data:

{
  id:          1
  name:        "Entity1"
  description: "Test entity no. 1"
  properties:  {
    color:        "red"
    lenght:       120
    width:        3.1882420
    hassomething: true
    country:      "Belgium"
  } 
}

Janten, ayeuna urang gaduh data anu sami pikeun duanana pilihan. Hayu urang mimitian ngabandingkeun palaksanaan dina kahirupan nyata!

Nyederhanakeun rarancang

Ieu disebutkeun tadi yén desain database ieu nyata disederhanakeun: hiji méja, ngagunakeun kolom JSONB pikeun sipat, tinimbang tilu tabel pikeun EAV. Tapi kumaha ieu ditarjamahkeun kana patarosan? Ngamutahirkeun sipat éntitas tunggal sapertos kieu:

-- EAV
UPDATE entity_attribute_value 
SET value = 'blue' 
WHERE entity_attribute_id = 1 
  AND entity_id = 120;

-- JSONB
UPDATE entity_jsonb 
SET properties = jsonb_set(properties, '{"color"}', '"blue"') 
WHERE id = 120;

Sakumaha anjeun tiasa tingali, pamundut panungtungan teu kasampak basajan. Pikeun ngapdet nilai sipat dina hiji objek JSONB, urang kedah nganggo fungsi jsonb_set(), sarta kudu lulus nilai anyar urang salaku obyék JSONB. Nanging, urang henteu kedah terang idéntifikasi sateuacanna. Ningali conto EAV, urang kedah terang boh entity_id sareng entity_attribute_id pikeun ngalaksanakeun pembaruan. Upami anjeun hoyong ngamutahirkeun sipat dina kolom JSONB dumasar kana nami obyék, éta sadayana dilakukeun dina hiji garis anu sederhana.

Ayeuna hayu urang pilih éntitas anu nembé kami diropéa dumasar kana warna anyarna:

-- EAV
SELECT e.name 
FROM entity e 
  INNER JOIN entity_attribute_value eav ON e.id = eav.entity_id
  INNER JOIN entity_attribute ea ON eav.entity_attribute_id = ea.id
WHERE ea.name = 'color' AND eav.value = 'blue';

-- JSONB
SELECT name 
FROM entity_jsonb 
WHERE properties ->> 'color' = 'blue';

Jigana urang bisa satuju yén kadua pondok (tanpa gabung!) Ku kituna leuwih dibaca. JSONB meunang di dieu! Kami nganggo operator JSON ->> pikeun nyandak warna salaku nilai téks tina objék JSONB. Aya ogé cara kadua pikeun ngahontal hasil anu sami dina modél JSONB nganggo operator @>:

-- JSONB 
SELECT name 
FROM entity_jsonb 
WHERE properties @> '{"color": "blue"}';

Ieu bit leuwih pajeulit: urang pariksa naha objék JSON dina kolom sipat ngandung obyék di sisi katuhu operator @>. Kirang dibaca, langkung berprestasi (tingali di handap).

Hayu urang saderhanakeun nganggo JSONB langkung jauh nalika anjeun kedah milih sababaraha sipat sakaligus. Ieu dimana pendekatan JSONB bener-bener bersinar: urang ngan saukur milih sipat salaku kolom tambahan dina set hasil urang, tanpa peryogi ngagabung:

-- JSONB 
SELECT name
  , properties ->> 'color'
  , properties ->> 'country'
FROM entity_jsonb 
WHERE id = 120;

Kalayan EAV, anjeun peryogi dua gabungan pikeun unggal harta anu anjeun hoyong naroskeun. Dina pamanggih kuring, patarosan di luhur nunjukkeun nyederhanakeun anu penting dina desain database. Anjeun ogé tiasa ningali seueur conto kumaha cara nyerat patarosan JSONB ieu pos.
Ayeuna waktuna ngobrol ngeunaan kinerja.

kakuwatan keur ngasilkeun

Pikeun ngabandingkeun kinerja I dipaké NERANGKEUN ANALISIS dina queries keur ngitung waktu palaksanaan. Unggal query dieksekusi sahenteuna tilu kali, sabab Nu Ngarencana query nyokot leuwih lila kahiji kalina. Kahiji, abdi lumpat queries tanpa indexes nanaon. Ieu écés dilayanan salaku kaunggulan JSONB, saprak ngagabung diperlukeun pikeun EAV teu bisa ngagunakeun indéks (widang konci asing teu indéks). Saatos éta, kuring nyiptakeun indéks dina dua kolom konci asing tina tabel nilai EAV, ogé indéks Gin pikeun kolom JSONB.

Pembaruan data nunjukkeun hasil waktos di handap ieu (dina mdet). Catet yén skalana logaritmik:

Ngaganti EAV sareng JSONB dina PostgreSQL

Kami ningali yén JSONB sacara signifikan (> 50000x) langkung gancang tibatan EAV tanpa indéks, kusabab alesan anu disebatkeun di luhur. Nalika urang indéks kolom konci primér, bédana ampir ngaleungit, tapi JSONB masih 1,3x gancang ti EAV. Catet yén indéks dina kolom JSONB teu aya pangaruhna di dieu, sabab kami henteu nganggo kolom harta dina kriteria evaluasi.

Pikeun pilihan data dumasar kana nilai sipat, urang meunang hasil di handap ieu (skala normal):

Ngaganti EAV sareng JSONB dina PostgreSQL

Anjeun tiasa ningali yén JSONB deui langkung gancang tibatan EAV tanpa indéks, tapi nalika EAV diindeks, éta langkung gancang tibatan JSONB. Tapi teras kuring ningali yén waktos pikeun queries JSONB sami, anu nyababkeun kuring kanyataan yén indéks GIN henteu dipicu. Tétéla, mun anjeun ngagunakeun indéks GIN dina kolom mibanda sipat Asezare populata, éta ngan mawa pangaruh nalika ngagunakeun @> operator inklusi. I dipaké ieu dina test anyar, sarta miboga dampak badag dina waktu: ngan 0,153 mdet! Éta 15000 kali langkung gancang tibatan EAV sareng 25000 kali langkung gancang tibatan operator ->>.

Jigana éta geulis gancang!

Ukuran tabel database

Hayu urang ngabandingkeun ukuran tabel pikeun duanana pendekatan. Dina psql, urang tiasa ningalikeun ukuran sadaya tabel sareng indéks nganggo paréntah dti+

Ngaganti EAV sareng JSONB dina PostgreSQL

Kalayan pendekatan EAV, ukuran méja sakitar 3068 MB, sareng indéksna dugi ka 3427 MB, jumlahna aya 6,43 GB. Ngagunakeun pendekatan JSONB, tabél ngagunakeun 1817 MB jeung indéks 318 MB, jumlahna aya 2,08 GB. Éta sapertilu tina ukuranana! Kanyataan ieu kaget kuring saeutik, saprak urang nyimpen ngaran harta di unggal objék JSONB.

Tapi angka nyarita sorangan: dina EAV, urang nyimpen dua integer konci asing per nilai atribut, hasilna 8 bait data tambahan. Salajengna, dina EAV, sadaya nilai properti disimpen salaku téks, sedengkeun JSONB bakal ngagunakeun nilai numerik sareng logis sacara internal upami mungkin, hasilna tapak suku anu langkung alit.

hasil

Gemblengna, Jigana nyimpen sipat éntitas dina format JSONB nyata bisa simplify rarancang jeung perawatan database Anjeun. Upami anjeun ngalakukeun seueur patarosan, nyimpen sadayana dina méja anu sami sareng éntitas bakal langkung éfisién. Kanyataan yén éta nyederhanakeun interaksi data parantos janten tambah, tapi databés anu dihasilkeun ogé ukuranana tilu kali langkung alit.

Ogé, dumasar kana hasil patokan, urang tiasa nyimpulkeun yén pinalti kinerja sakedik pisan. Dina sababaraha kasus, JSONB malah ngajalankeun leuwih gancang ti EAV, sahingga malah hadé. Tapi, patokan ieu pasti henteu nutupan sagala aspek (contona, éntitas anu ngagaduhan jumlah anu ageung pisan, paningkatan anu signifikan dina jumlah sipat dina data anu tos aya, jsb.), janten upami anjeun gaduh saran pikeun perbaikan, mangga tinggalkeun aranjeunna dina koméntar!

sumber: www.habr.com

Mésér hosting anu dipercaya pikeun situs anu gaduh panyalindungan DDoS, server VPS VDS 🔥 Meser hosting situs wéb anu tiasa dipercaya nganggo panyalindungan DDoS, server VPS VDS | ProHoster