TL; DR: JSONB mund të thjeshtojë ndjeshëm zhvillimin e skemës së DB pa e sakrifikuar performancën në pyetje.
Hyrje
Le të kontribuojmë një shembull klasik, ndoshta një nga përdorimet më të vjetra në botën e DB-ve relacional (database): kemi një entitet dhe duhet të ruajmë disa veti (atribute) të këtij entiteti. Por jo të gjitha instancat mund të kenë të njëjtin set vetish, dhe gjithashtu në të ardhmen, mund të ketë shtim të vetive të tjera.
Mënyra më e thjeshtë për të zgjidhur këtë problem është të krijoni një kolonë në tabelën e DB-së për çdo vlerë të vetisë, dhe thjesht të plotësoni ato që nevojiten për një instancë të caktuar të entitetit. Super! Problemi është zgjidhur⊠deri në momentin kur tabela juaj përmban miliona regjistrime dhe ju nevojitet të shtoni një regjistrim të ri.
Le tĂ« shqyrtojmĂ« modelin EAV (), ai takon shpesh. NjĂ« tavolinĂ« pĂ«rmban entitete (shĂ«nime), njĂ« tavolinĂ« tjetĂ«r pĂ«rmban emrat e vetive (atributeve), dhe tavolina e tretĂ« lidh entitetet me atributet e tyre dhe mban vlerĂ«n e kĂ«tyre atributeve pĂ«r entitetin aktual. Kjo ju ofron mundĂ«sinĂ« pĂ«r tĂ« pasur grupe tĂ« ndryshme vetish pĂ«r objekte tĂ« ndryshme, si dhe pĂ«r tĂ« shtuar vetitĂ« ânĂ« flakĂ«â, pa ndryshuar strukturat e DB-sĂ«.
MegjithatĂ«, nuk do tĂ« shkruaja kĂ«tĂ« shĂ«nim nĂ«se nuk do tĂ« ishin disa mangĂ«si nĂ« qasjen qĂ« pĂ«rdor EVA. PĂ«r shembull, pĂ«r tĂ« marrĂ« njĂ« ose mĂ« shumĂ« entitete qĂ« kanĂ« nga 1 atribut kĂ«rkohen 2 bashkime nĂ« kĂ«rkesĂ«: e para â bashkimi me tavolinĂ«n e atributeve, e dyta â bashkimi me tavolinĂ«n e vlerave. NĂ«se entitetet kanĂ« 2 atribute, atĂ«herĂ« nevojiten tashmĂ« 4 bashkime! PĂ«r mĂ« tepĂ«r, tĂ« gjitha atributet zakonisht ruhen si vargje, çka rezulton nĂ« konvertimin e tipeve pĂ«r si pĂ«r rezultatin ashtu edhe pĂ«r kushtin WHERE. NĂ«se shkruani shumĂ« kĂ«rkesa, kjo Ă«shtĂ« mjaft Shpenzuese, sa i pĂ«rket pĂ«rdorimit tĂ« burimeve.
Megjithatë, pavarësisht këtyre mangësive të dukshme, EAV ka qenë prej kohësh në përdorim për zgjidhjen e këtij lloji problemi. Këto ishin mangësi të pashmangshme, dhe nuk kishte një alternativë më të mirë.
Por pastaj nĂ« PostgreSQL u shfaq njĂ« 'teknologji' e reâŠ
Që nga PostgreSQL 9.4, u shtua një tip të dhënash JSONB për të ruajtur të dhënat njëshe në formatin JSON. Ndërsa ruajtja e JSON në këtë format zakonisht kërkon pak më shumë hapësirë dhe kohë se sa JSON-i i thjeshtë tekstor, operacionet me të ndodhin shumë më shpejt. Po ashtu, JSONB mbështet indeksimin, gjë që i bën kërkesat ndaj tij edhe më të shpejta.
Tipi i të dhënave JSONB na lejon të zëvendësojmë modelin e rëndë EAV duke shtuar vetëm një kolonë JSONB në tabelën tonë të entiteteve, çka e thjeshton ndjeshëm projektimin e bazës së të dhënave. Por shumë njerëz pretendojnë se kjo duhet të shoqërohet me një rënie të performancës⊠Për këtë arsye shkruhet ky artikull.
Konfigurimi i bazës së të dhënave për testim
Për këtë krahasim, kam krijuar një bazë të dhënash në një instalim të ri të PostgreSQL 9.5 në një konfigurim prej 80 dollarësh. Ubuntu 14.04. Pasi kam vendosur disa parametra në postgresql.conf, kam nisur skripti me psql. Për të paraqitur të dhënat në formatin EAV, u krijuan tabelat e mëposhtme:
KRIJO TABELĂ enti (
id SERIAL KRYESORE,
emri TEKST,
përshkrimi TEKST
);
KRIJO TABELĂ entity_attribute (
id SERIAL KRYESORE,
emri TEKST
);
KRIJO TABELĂ entity_attribute_value (
id SERIAL KRYESORE,
entity_id INT REFERENCAT entity(id),
entity_attribute_id INT REFERENCAT entity_attribute(id),
vlera TEKST
);
MĂ« poshtĂ« Ă«shtĂ« tabela ku do tĂ« ruhen tĂ« njĂ«jtat tĂ« dhĂ«na, por me atribute nĂ« kolonĂ«n e tipit JSONB â propertietet.
KRIJO TABELĂ entity_jsonb (
id SERIAL KRYESORE,
emri TEKST,
përshkrimi TEKST,
propertietet JSONB
);
Duket shumĂ« mĂ« e thjeshtĂ«, apo jo? MĂ« pas u shtuan nĂ« tabelat e entiteteve (enti & entity_jsonb) 10 milijon regjistrime, dhe si pĂ«rfundim, u plotĂ«suan tĂ« dhĂ«nat e njĂ«jta tabelave qĂ« pĂ«rdorin modelin EAV dhe qasjen me kolonĂ«n JSONB â entity_jsonb.propertietet. KĂ«shtu, morĂ«m disa lloje tĂ« ndryshme tĂ« tĂ« dhĂ«nave nĂ« tĂ«rĂ« grupin e pronave. Shembuj tĂ« dhĂ«nash:
{
id: 1
emri: "Entity1"
përshkrimi: "Entitet testi nr. 1"
propertietet: {
ngjyra: "e kuqe"
gjatësia: 120
gjerësi: 3.1882420
kaçkavall: true
vendi: "Belgjika"
}
}Pra, tani kemi të dhëna të njëjta për dy variante. Le të fillojmë të krahasojmë realizimet në punë!
Thjeshtimi i dizajnit
Kemi thënë më parë se dizajni i DB është simplifikuar ndjeshëm: një tabelë, përmes përdorimit të kolonës JSONB për vetitë, në vend të përdorimit të tre tabelave për EAV. Por si reflektohet kjo në pyetje?
-- 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;
Siç e shohim, kĂ«rkesa e fundit nuk duket mĂ« e thjeshtĂ«. QĂ« tĂ« pĂ«rditĂ«sojmĂ« vlerĂ«n e njĂ« vetie nĂ« objektin JSONB, ne duhet tĂ« pĂ«rdorim funksionin , dhe duhet tĂ« kalojmĂ« vlerĂ«n tonĂ« tĂ« re si njĂ« objekt JSONB. MegjithatĂ«, nuk na nevojitet tĂ« dimĂ« ndonjĂ« identifikues paraprakisht. Duke parĂ« shembullin me EAV, na nevojitet tĂ« dimĂ« si entity_id, ashtu edhe entity_attribute_id pĂ«r tĂ« kryer pĂ«rditĂ«simin. NĂ«se dĂ«shiron tĂ« pĂ«rditĂ«sosh njĂ« vetie nĂ« kolonĂ«n JSONB bazuar nĂ« emrin e objektit, â kjo bĂ«het me njĂ« rresht tĂ« thjeshtĂ«.
Tani le të zgjedhim atë entitet që sapo e përditësuam, sipas kushtit të ngjyrës së tij të re:
-- 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';
Mendoj se se mund të pajtohemi që e dyta është më e shkurtër (pa join!), dhe për rrjedhojë më e lexueshme. Këtu fiton JSONB! Ne e përdorim operatorin JSON ->>, për të marrë ngjyrën si vlerë tekstuale nga objekti JSONB. Ekziston gjithashtu një mënyrë tjetër për të arritur të njëjtin rezultat në modelin JSONB duke përdorur operatorin @>:
-- JSONB
SELECT name
FROM entity_jsonb
WHERE properties @> '{"color": "blue"}';
Kjo është pak më e komplikuar: ne kontrollojmë nëse objekti JSON në kolonën e pronave përmban objektin që ndodhet në të djathtë të operatorit @>. Më pak e lexueshme, më e prodhueshme (shih më poshtë).
Le të thjeshtojmë përdorimin e JSONB edhe më shumë, kur ju nevojitet të zgjidhni disa prona në të njëjtën kohë. Këtu është ku i përshtatet vërtet qasja JSONB: ne thjesht zgjedhim pronat si kolona shtesë në grupin tonë të rezultateve pa nevojën për bashkime:
-- JSONB
SELECT name
, properties ->> 'color'
, properties ->> 'country'
FROM entity_jsonb
WHERE id = 120;
Me EAV nevojiten 2 bashkime për çdo pronë që dëshironi të kërkoni. Sipas mendimit tim, kërkesat e lartpërmendura tregojnë një thjeshtim të madh në dizajnin e bazës së të dhënave. Shihni më shumë shembuj se si të shkruani kërkesa për JSONB, gjithashtu në post.
Tani është koha për të folur rreth performancës.
Performanca
PĂ«r tĂ« krahasuar performancĂ«n, pĂ«rdora nĂ« kĂ«rkesat, pĂ«r tĂ« llogaritur kohĂ«n e ekzekutimit. Ădo kĂ«rkesĂ« u ekzekutua tĂ« paktĂ«n tre herĂ«, sepse hera e parĂ« planifikuesit e kĂ«rkesave kĂ«rkon mĂ« shumĂ« kohĂ«. E fillova duke ekzekutuar kĂ«rkesat pa ndonjĂ« indeks. Pa dyshim, kjo i shĂ«rbente JSONB, pasi bashkimet e nevojshme pĂ«r EAV nuk mund tĂ« pĂ«rdorin indekset (fushat e çelĂ«sit tĂ« jashtĂ«m nuk ishin indeksuar). Pas kĂ«saj, krijova njĂ« indeks pĂ«r 2 kolonat e çelĂ«save tĂ« jashtĂ«m nĂ« tabelĂ«n EAV si dhe njĂ« indeks pĂ«r kolonĂ«n JSONB.
Rritjet e të dhënave treguan rezultatet e mëposhtme në kohë (në ms). Kushtojini vëmendje se shkalla është logarithmike:

Shohim se se JSONB është shumë më i shpejtë (> 50000-x) se EAV, nëse nuk përdoren indekset, për arsye të përmendura më sipër. Kur indeksojmë kolonat me çelësa primarë, diferenca pothuajse zhduket, por JSONB është akoma 1,3 herë më i shpejtë se EAV. Vini re se indeksi në kolonën JSONB këtu nuk ka asnjë ndikim, pasi nuk e përdorim kolonën e pronave në kriteret e vlerësimit.
Për të zgjedhur të dhënat mbi bazën e një vlere të pronës, kemi rezultatet e mëposhtme (shkallë normale):

VĂ«rehet se JSONB pĂ«rsĂ«ri funksionon mĂ« shpejt se EAV pa indekse, por kur EAV ka indekse â akoma funksionon mĂ« shpejt se JSONB. Por mĂ« pas pashĂ« se koha pĂ«r kĂ«rkesat JSONB ishte e njĂ«jtĂ«, kjo mĂ« shtyu nĂ« faktin se indekset GIN nuk punonin. Duket se kur pĂ«rdorni indeksin GIN pĂ«r njĂ« kolonĂ« me pronat e mbushura, ai vepron vetĂ«m kur pĂ«rdoret operatori i pĂ«rfshirjes @>. E pĂ«rdora kĂ«tĂ« nĂ« njĂ« test tĂ« ri, i cili kishte njĂ« ndikim tĂ« madh nĂ« kohĂ«n: vetĂ«m 0,153 ms! Kjo Ă«shtĂ« 15000 herĂ« mĂ« e shpejtĂ« se EAV, dhe 25000 herĂ« mĂ« e shpejtĂ« se operatori ->>.
Mendoj se ishte mjaft shpejt!
Madhësia e tabelave të BD
Le të krahasojmë përmasat e tabelave në të dy qasjet. Në psql mund të tregojmë madhësinë e të gjitha tabelave dhe indekseve përmes komandës dti+

Për qasjen EAV, përmasat e tabelave arrijnë rreth 3068 MB, ndërsa indekset deri në 3427 MB, duke dhënë gjithsej 6,43 GB. Duke përdorur qasjen me JSONB, përdoren 1817 MB për tabelën dhe 318 MB për indekset, duke bërë gjithsej 2,08 GB. Kjo është tri herë më pak! Ky fakt më befasoi pak, sepse ne ruajmë emrat e pronave në çdo objekt JSONB.
Por numrat flasin vetë: në EAV ne ruajmë 2 çelësa të jashtëm të plotë mbi vlerën e atributit, duke rezultuar në 8 byte të dhëna shtesë. Për më tepër, në EAV të gjitha vlerat e pronave ruhen si tekst, ndërsa JSONB do të përdorë vlera numerike dhe logjike brenda, ku është e mundur, duke rezultuar në një volum më të vogël.
Përfundimet
Në përgjithësi, mendoj se ruajtja e vetive të entiteteve në formatin JSONB mund të thjeshtojë ndjeshëm dizenjimin dhe mirëmbajtjen e bazës suaj të të dhënave. Nëse bëni shumë kërkesa, atëherë gjithçka që ruhet në një tabelë me entitetin do të funksionojë vërtet më efektivisht. Dhe fakti që kjo e thjeshton ndërveprimin midis të dhënave është një përfitim, por gjithashtu baza e të dhënave rezultuese është 3 herë më e vogël në volum.
Gjithashtu, sipas testeve të kryera, mund të përfundosh se humbjet e performancës janë shumë të pakta. Në disa raste, JSONB madje funksionon më shpejt se EAV, gjë që e bën atë edhe më të mirë. Megjithatë, ky test standard natyrisht nuk kap të gjitha aspektet (për shembull, entitete me një numër shumë të madh vetish, rritje të konsiderueshme të numrit të vetive të të dhënave ekzistuese,...), ndaj, nëse keni ndonjë sugjerim se si t'i përmirësoni ato, ju lutemi mos ngurroni të lini komentet tuaja!
Burimi: habr.com
