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 (), é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. Ubuntu 14.04. После настройки некоторых параметров в postgresql.conf я запустил 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 , 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 pos.
Ayeuna waktuna ngobrol ngeunaan kinerja.
kakuwatan keur ngasilkeun
Pikeun ngabandingkeun kinerja I dipaké 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 pikeun kolom JSONB.
Pembaruan data nunjukkeun hasil waktos di handap ieu (dina mdet). Catet yén skalana logaritmik:

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):

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+

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
