TL; DR: JSONB vÔib oluliselt lihtsustada andmebaasi skeemi arendamist ilma pÀringu jÔudlust ohverdamata.
Sissejuhatus
Toome klassikalise nĂ€ite ilmselt ĂŒhest vanimast kasutusjuhtumist maailmas relatsiooniandmebaasi (andmebaasi) kohta: meil on olem ja me peame salvestama selle olemi teatud atribuudid (atribuudid). Kuid mitte kĂ”igil eksemplaridel ei pruugi olla sama atribuutide komplekt ja tulevikus vĂ”idakse lisada rohkem atribuute.
Lihtsaim viis selle probleemi lahendamiseks on luua andmebaasi tabelis iga atribuudi vÀÀrtuse jaoks veerg ja lihtsalt tÀita need, mis on konkreetse olemi eksemplari jaoks vajalikud. SuurepÀrane! Probleem lahendatud... kuni teie tabel sisaldab miljoneid kirjeid ja peate lisama uue kirje.
MĂ”elge EAV mustrile (), esineb seda ĂŒsna sageli. Ăks tabel sisaldab oleme (kirjeid), teine ââtabel sisaldab atribuutide nimesid (atribuute) ja kolmas tabel seob olemid nende atribuutidega ja sisaldab nende atribuutide vÀÀrtust praeguse olemi jaoks. See annab teile vĂ”imaluse omada erinevate objektide jaoks erinevaid atribuutide komplekte ja lisada atribuute kĂ€igupealt ilma andmebaasi struktuuri muutmata.
Kuid ma ei kirjutaks seda postitust, kui EVA-lĂ€henemisel poleks mingeid varjukĂŒlgi. NĂ€iteks ĂŒhe vĂ”i mitme olemi saamiseks, millel on igaĂŒhel 1 atribuut, on pĂ€ringus vaja 2 ĂŒhendust: esimene on ĂŒhendus atribuutide tabeliga, teine ââââon vÀÀrtuste tabeliga ĂŒhendamine. Kui olemil on 2 atribuuti, siis on vaja 4 liitumist! Lisaks salvestatakse kĂ”ik atribuudid tavaliselt stringidena, mille tulemuseks on nii tulemuse kui ka WHERE-klausli tĂŒĂŒbi ĂŒlekandmine. Kui kirjutate palju pĂ€ringuid, on see ressursikasutuse seisukohalt ĂŒsna raiskav.
Vaatamata nendele ilmsetele puudustele on EAV-d seda tĂŒĂŒpi probleemide lahendamiseks pikka aega kasutatud. Need olid vĂ€ltimatud puudused ja paremat alternatiivi lihtsalt polnud.
Kuid siis ilmus PostgreSQL-i uus "tehnoloogia" ...
Alates versioonist PostgreSQL 9.4 lisati JSON-i binaarandmete salvestamiseks JSONB andmetĂŒĂŒp. Kuigi JSON-i salvestamine selles vormingus vĂ”tab tavaliselt veidi rohkem ruumi ja aega kui lihtteksti JSON-i, on sellega toimingute tegemine palju kiirem. JSONB toetab ka indekseerimist, mis muudab pĂ€ringud veelgi kiiremaks.
JSONB andmetĂŒĂŒp vĂ”imaldab meil tĂŒlika EAV mustri asendada, lisades meie olemitabelisse vaid ĂŒhe JSONB veeru, mis lihtsustab oluliselt andmebaasi ĂŒlesehitust. Kuid paljud vaidlevad vastu, et sellega peaks kaasnema tootlikkuse langus... SellepĂ€rast ma selle artikli kirjutasin.
Testide andmebaasi loomine
Selle vÔrdluse jaoks lÔin andmebaasi PostgreSQL 9.5 vÀrske installi kohta 80-dollarise jÀrguga Ubuntu 14.04 PÀrast postgresql.conf failis mÔnede parameetrite konfigureerimist kÀivitasin skript psql-i abil. Andmete esitamiseks EAV kujul koostati jÀrgmised tabelid:
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
);
Allpool on tabel, kuhu salvestatakse samad andmed, kuid atribuutidega JSONB tĂŒĂŒpi veerus â omadused.
CREATE TABLE entity_jsonb (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
properties JSONB
);
Tundub palju lihtsam, kas pole? SeejĂ€rel lisati see olemitabelitesse (ĂŒksus & entity_jsonb) 10 miljonit kirjet ja vastavalt sellele tĂ€ideti tabel samade andmetega, kasutades EAV mustrit ja lĂ€henemist JSONB veeruga - entity_jsonb.properties. Seega saime kogu atribuutide hulgast mitu erinevat andmetĂŒĂŒpi. NĂ€idisandmed:
{
id: 1
name: "Entity1"
description: "Test entity no. 1"
properties: {
color: "red"
lenght: 120
width: 3.1882420
hassomething: true
country: "Belgium"
}
}NĂŒĂŒd on meil mĂ”lema variandi kohta samad andmed. Alustame juurutuste vĂ”rdlemist tööl!
Lihtsustage oma disaini
Varem vĂ€ideti, et andmebaasi kujundust on oluliselt lihtsustatud: ĂŒks tabel, kasutades atribuutide jaoks JSONB veergu, selle asemel, et kasutada EAV jaoks kolme tabelit. Aga kuidas see taotlustes kajastub? Ăhe olemi atribuudi vĂ€rskendamine nĂ€eb vĂ€lja jĂ€rgmine:
-- 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;
Nagu nĂ€ete, ei tundu viimane taotlus lihtsam. JSONB-objekti atribuudi vÀÀrtuse vĂ€rskendamiseks peame kasutama funktsiooni ja peaks edastama meie uue vÀÀrtuse JSONB-objektina. Kuid me ei pea teadma ĂŒhtegi identifikaatorit ette. Vaadates EAV nĂ€idet, peame vĂ€rskenduse tegemiseks teadma nii atribuuti entity_id kui ka entity_attribute_id. Kui soovite vĂ€rskendada JSONB veerus asuvat atribuuti objekti nime alusel, tehakse seda kĂ”ik ĂŒhe lihtsa reaga.
NĂŒĂŒd valime Ă€sja vĂ€rskendatud olemi selle uue vĂ€rvi pĂ”hjal:
-- 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';
Arvan, et vĂ”ime nĂ”ustuda, et teine ââon lĂŒhem (no join!) ja seetĂ”ttu loetavam. JSONB vĂ”idab siin! VĂ€rvi saamiseks JSONB-objektilt tekstivÀÀrtusena kasutame operaatorit JSON ->>. JSONB-mudelis on sama tulemuse saavutamiseks ka teine ââviis, kasutades operaatorit @>:
-- JSONB
SELECT name
FROM entity_jsonb
WHERE properties @> '{"color": "blue"}';
See on veidi keerulisem: kontrollime, kas JSON-objekt selle atribuutide veerus sisaldab objekti, mis asub operaatorist @> paremal. VĂ€hem loetav, produktiivsem (vt allpool).
Teeme JSONB kasutamise veelgi lihtsamaks, kui peate valima mitu atribuuti korraga. Siin tulebki JSONB-lÀhenemine tÔeliselt kasutusele: me lihtsalt valime atribuudid oma tulemuste komplekti tÀiendavate veergudena, ilma et oleks vaja liite:
-- JSONB
SELECT name
, properties ->> 'color'
, properties ->> 'country'
FROM entity_jsonb
WHERE id = 120;
EAV-ga on teil vaja 2 liitumist iga kinnisvara kohta, mida soovite pĂ€rida. Minu arvates nĂ€itavad ĂŒlaltoodud pĂ€ringud andmebaasi kujundamise suurt lihtsustamist. Vaadake rohkem nĂ€iteid selle kohta, kuidas kirjutada pĂ€ringuid JSONB-le, mis on samuti vĂ”imalik postitus.
NĂŒĂŒd on aeg rÀÀkida jĂ”udlusest.
ĐŃĐŸĐžĐ·ĐČĐŸĐŽĐžŃДлŃĐœĐŸŃŃŃ
Kasutasin jÔudluse vÔrdlemiseks pÀringutes tÀitmisaja arvutamiseks. Iga pÀring tÀideti vÀhemalt kolm korda, kuna pÀringuplaneerija vÔtab esimesel korral kauem aega. KÔigepealt kÀivitasin pÀringud ilma indeksiteta. Ilmselgelt oli see JSONB eelis, kuna EAV jaoks vajalikud liitumised ei saanud kasutada indekseid (vÔÔra vÔtme vÀljad ei olnud indekseeritud). PÀrast seda lÔin indeksi EAV vÀÀrtuste tabeli kahele vÔÔrvÔtme veerule ja indeksi JSONB veeru jaoks.
AndmevÀrskendus nÀitas ajaliselt (ms) jÀrgmisi tulemusi. Pange tÀhele, et skaala on logaritmiline:

NĂ€eme, et JSONB on palju (> 50000-x) kiirem kui EAV, kui te indekseid ei kasuta, ĂŒlaltoodud pĂ”hjusel. Kui indekseerime primaarvĂ”tmetega veerge, siis erinevus peaaegu kaob, kuid JSONB on siiski 1,3 korda kiirem kui EAV. Pange tĂ€hele, et JSONB veeru indeks ei oma siin mingit mĂ”ju, kuna me ei kasuta hindamiskriteeriumides atribuutide veergu.
Kinnisvara vÀÀrtuse pÔhjal andmete valimisel saame jÀrgmised tulemused (tavaskaala):

VÔite mÀrgata, et JSONB töötab jÀlle kiiremini kui EAV ilma indeksiteta, kuid kui EAV koos indeksitega, töötab see ikkagi kiiremini kui JSONB. Kuid siis nÀgin, et JSONB-pÀringute ajad olid samad, see ajendas mind tÔsiasjale, et GIN-indeksid ei tööta. Ilmselt, kui kasutate GIN-indeksit tÀidetud atribuutidega veerus, jÔustub see ainult siis, kui kasutate kaasamisoperaatorit @>. Kasutasin seda uues testis ja sellel oli ajale tohutu mÔju: ainult 0,153 ms! See on 15000 25000 korda kiirem kui EAV ja XNUMX XNUMX korda kiirem kui operaator ->>.
Ma arvan, et see oli piisavalt kiire!
Andmebaasi tabeli suurus
VÔrdleme mÔlema lÀhenemisviisi tabeli suurusi. Psql-is saame kÀsu abil nÀidata kÔigi tabelite ja indeksite suurust dti+

EAV-meetodi puhul on tabelite suurus umbes 3068 MB ja indeksid kuni 3427 MB, kokku 6,43 GB. JSONB lĂ€henemisviis kasutab tabeli jaoks 1817 MB ja indeksite jaoks 318 MB, mis on 2,08 GB. Selgub 3 korda vĂ€hem! See asjaolu ĂŒllatas mind veidi, kuna salvestame atribuutide nimed igas JSONB-objektis.
Kuid siiski rÀÀgivad numbrid enda eest: EAV-s salvestame iga atribuudi vÀÀrtuse kohta 2 tÀisarvulist vÔÔrvÔtit, mille tulemuseks on 8 baiti lisaandmeid. Lisaks salvestab EAV kÔik atribuutide vÀÀrtused tekstina, samas kui JSONB kasutab vÔimaluse korral sisemiselt arvulisi ja tÔevÀÀrtusi, mille tulemuseks on vÀiksem jalajÀlg.
Tulemused
Ăldiselt arvan, et olemi atribuutide salvestamine JSONB-vormingus vĂ”ib teie andmebaasi kujundamise ja haldamise palju lihtsamaks muuta. Kui kĂ€itate palju pĂ€ringuid, toimib olemiga kĂ”ike samas tabelis hoidmine tegelikult tĂ”husamalt. Ja see, et see lihtsustab andmete omavahelist suhtlust, on juba pluss, kuid tulemuseks olev andmebaas on mahult 3 korda vĂ€iksem.
Samuti vĂ”ime tehtud testide pĂ”hjal jĂ€reldada, et jĂ”udluskaod on vĂ€ga ebaolulised. MĂ”nel juhul on JSONB isegi kiirem kui EAV, muutes selle veelgi paremaks. Kuid see vĂ”rdlusalus ei hĂ”lma muidugi kĂ”iki aspekte (nt vĂ€ga suure hulga atribuutidega ĂŒksused, olemasolevate andmete atribuutide arvu mĂ€rkimisvÀÀrne suurenemine jne), nii et kui teil on ettepanekuid nende parandamiseks , jĂ€tke julgelt kommentaaridesse!
Allikas: www.habr.com
