TL;DR: JSONB mund ta thjeshtojë shumë zhvillimin e skemës së bazës së të dhënave pa sakrifikuar performancën e pyetjeve.
Paraqitje
Le të marrim një shembull klasik, ndoshta një nga rastet më të vjetra të përdorimit në botën e bazave të të dhënave relacionale: kemi një entitet dhe duhet të ruajmë veti (atribute) të caktuara të këtij entiteti. Por jo të gjitha instancat mund të kenë të njëjtin grup vetish dhe mund të shtohen më shumë veti në të ardhmen.
Zgjidhja më e thjeshtë për këtë problem është të krijoni një kolonë në tabelën e bazës së të dhënave për secilën vlerë të vetisë dhe thjesht të plotësoni ato të nevojshme për një instancë specifike të entitetit. Shkëlqyeshëm! Problemi u zgjidh... derisa tabela juaj të përmbajë miliona regjistrime dhe ju duhet të shtoni një regjistrim të ri.
Le të shqyrtojmë modelin EAV (), është mjaft e zakonshme. Një tabelë përmban entitete (regjistrime), një tabelë tjetër përmban emrat e vetive (atributet) dhe një tabelë e tretë lidh entitetet me atributet e tyre dhe përmban vlerat e këtyre atributeve për entitetin aktual. Kjo ju lejon të keni grupe të ndryshme vetish për objekte të ndryshme, si dhe të shtoni veti menjëherë, pa ndryshuar strukturën e bazës së të dhënave.
Megjithatë, nuk do ta shkruaja këtë postim nëse nuk do të kishte disa të meta në qasjen EVA. Për shembull, marrja e një ose më shumë entiteteve me nga një atribut secili kërkon dy bashkime në pyetje: bashkimi i parë me tabelën e atributeve, bashkimi i dytë me tabelën e vlerave. Nëse një entitet ka dy atribute, atëherë kërkohen katër bashkime! Për më tepër, të gjitha atributet zakonisht ruhen si vargje, gjë që rezulton në detyrim të tipit si për rezultatin ashtu edhe për klauzolën WHERE. Nëse shkruani shumë pyetje, kjo është mjaft e kotë për sa i përket përdorimit të burimeve.
Pavarësisht këtyre mangësive të dukshme, EAV është përdorur prej kohësh për të zgjidhur këto lloj problemesh. Këto ishin mangësi të pashmangshme dhe thjesht nuk kishte një alternativë më të mirë.
Por mĂ« pas u shfaq njĂ« "teknologji" e re nĂ« PostgreSQLâŠ
Duke filluar me PostgreSQL 9.4, u shtua lloji i tĂ« dhĂ«nave JSONB pĂ«r ruajtjen e tĂ« dhĂ«nave binare JSON. Edhe pse ruajtja e JSON nĂ« kĂ«tĂ« format zakonisht kĂ«rkon pak mĂ« shumĂ« hapĂ«sirĂ« ââdhe kohĂ« sesa ruajtja e JSON me tekst tĂ« thjeshtĂ«, operacionet me tĂ« janĂ« shumĂ« mĂ« tĂ« shpejta. JSONB gjithashtu mbĂ«shtet indeksimin, duke i bĂ«rĂ« pyetjet edhe mĂ« tĂ« shpejta.
Lloji i të dhënave JSONB na lejon të zëvendësojmë modelin e rëndë EAV duke shtuar vetëm një kolonë të vetme JSONB në tabelën tonë të entitetit, duke thjeshtuar ndjeshëm dizajnin e bazës së të dhënave. Megjithatë, shumë argumentojnë se kjo vjen me një kosto në performancë⊠Kjo është arsyeja pse shkrova këtë artikull.
Vendosja e një baze të dhënash testimi
Për këtë krahasim, krijova bazën e të dhënave në një instalim të ri të PostgreSQL 9.5 në versionin prej 80 dollarësh. Ubuntu 14.04 Pasi konfigurova disa parametra në postgresql.conf, ekzekutova skript duke përdorur psql. Për të paraqitur të dhënat si EAV, u krijuan tabelat e mëposhtme:
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
);
MĂ« poshtĂ« Ă«shtĂ« njĂ« tabelĂ« ku do tĂ« ruhen tĂ« njĂ«jtat tĂ« dhĂ«na, por me atribute nĂ« njĂ« kolonĂ« JSONB â Pronat.
CREATE TABLE entity_jsonb (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
properties JSONB
);
Duket shumĂ« mĂ« e thjeshtĂ«, apo jo? Pastaj u shtua nĂ« tabelat e entiteteve (entitet & entity_jsonb) 10 milionĂ« regjistrime, dhe nĂ« pĂ«rputhje me rrethanat, tabela u mbush me tĂ« dhĂ«na identike ku u pĂ«rdorĂ«n modeli EAV dhe qasja me kolonĂ«n JSONB â entity_jsonb.propertiesKĂ«shtu, morĂ«m disa lloje tĂ« ndryshme tĂ« tĂ« dhĂ«nave nĂ« tĂ« gjithĂ« grupin e vetive. Shembull tĂ« dhĂ«nash:
{
id: 1
name: "Entity1"
description: "Test entity no. 1"
properties: {
color: "red"
lenght: 120
width: 3.1882420
hassomething: true
country: "Belgium"
}
}Pra, tani kemi të dhëna identike për të dyja opsionet. Le të fillojmë të krahasojmë implementimet në jetën reale!
Thjeshtimi i dizajnit
Më parë u përmend se dizajni i bazës së të dhënave ishte thjeshtuar ndjeshëm: një tabelë, duke përdorur një kolonë JSONB për vetitë, në vend të tre tabelave për EAV. Por si përkthehet kjo në pyetje? Përditësimi i një vetie të vetme entiteti duket kështu:
-- 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ç mund ta shihni, pyetja e fundit nuk duket më e thjeshtë. Për të përditësuar vlerën e një vetie në një objekt JSONB, duhet të përdorim funksionin , dhe duhet ta kalojë vlerën tonë të re si një objekt JSONB. Megjithatë, nuk kemi nevojë të dimë ndonjë identifikues paraprakisht. Duke parë shembullin EAV, duhet të dimë si entity_id ashtu edhe entity_attribute_id për të kryer përditësimin. Nëse doni të përditësoni një veti në një kolonë JSONB bazuar në emrin e objektit, e gjitha bëhet në një rresht të thjeshtë.
Tani le të zgjedhim entitetin që sapo përditësuam bazuar në ngjyrën e 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 mund të biem dakord që e dyta është më e shkurtër (pa bashkimin!) dhe për këtë arsye më e lexueshme. JSONB fiton këtu! Ne përdorim operatorin JSON ->> për të marrë ngjyrën si vlerë teksti nga objekti JSONB. Ekziston edhe një mënyrë e dytë 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 vetive përmban objektin në anën e djathtë të operatorit @>. Më pak i lexueshëm, më shumë performancë (shih më poshtë).
Le ta thjeshtojmë edhe më shumë përdorimin e JSONB kur duhet të zgjidhni shumë veti njëkohësisht. Këtu shkëlqen vërtet qasja JSONB: ne thjesht zgjedhim vetitë si kolona shtesë në grupin tonë të rezultateve, pa pasur nevojë për bashkime:
-- JSONB
SELECT name
, properties ->> 'color'
, properties ->> 'country'
FROM entity_jsonb
WHERE id = 120;
Me EAV, do t'ju duhen dy bashkime për secilën veti që dëshironi të pyetni. Sipas mendimit tim, pyetjet e mësipërme demonstrojnë një thjeshtëzim të konsiderueshëm në dizajnin e bazës së të dhënave. Gjithashtu mund të shihni më shumë shembuj se si të shkruani pyetje JSONB në postë.
Tani është koha të flasim për performancën.
prodhimtari
PĂ«r tĂ« krahasuar performancĂ«n qĂ« pĂ«rdora nĂ« pyetje pĂ«r tĂ« llogaritur kohĂ«n e ekzekutimit. Ădo pyetje u ekzekutua tĂ« paktĂ«n tre herĂ«, sepse planifikuesi i pyetjeve zgjat mĂ« shumĂ« herĂ«n e parĂ«. SĂ« pari, unĂ« i ekzekutova pyetjet pa indekse. Kjo padyshim shĂ«rbeu si njĂ« avantazh i JSONB, pasi bashkimet e kĂ«rkuara pĂ«r EAV nuk mund tĂ« pĂ«rdornin indekse (fushat e çelĂ«sit tĂ« huaj nuk ishin tĂ« indeksuara). Pas kĂ«saj, krijova njĂ« indeks nĂ« dy kolonat e çelĂ«sit tĂ« huaj tĂ« tabelĂ«s sĂ« vlerave EAV, si dhe njĂ« indeks pĂ«r kolonĂ«n JSONB.
Përditësimet e të dhënave treguan rezultatet e mëposhtme kohore (në ms). Vini re se shkalla është logaritmike:

Shohim që JSONB është dukshëm (>50000 herë) më i shpejtë se EAV pa indekse, për arsyen e përmendur më sipër. Kur indeksojmë kolonat e çelësit primar, ndryshimi pothuajse zhduket, por JSONB është ende 1,3 herë më i shpejtë se EAV. Vini re se indeksi në kolonën JSONB nuk ka efekt këtu, pasi nuk e përdorim kolonën e vetive në kriteret e vlerësimit.
Për përzgjedhjen e të dhënave bazuar në vlerën e pronës, marrim rezultatet e mëposhtme (shkalla normale):

Mund të shihni që JSONB është përsëri më i shpejtë se EAV pa indekse, por kur EAV indeksohet, është akoma më i shpejtë se JSONB. Por pastaj pashë që kohët për pyetjet JSONB ishin të njëjta, gjë që më çoi në faktin se indekset GIN nuk aktivizohen. Me sa duket, kur përdorni një indeks GIN në një kolonë me veti të populluara, ai hyn në fuqi vetëm kur përdorni operatorin e përfshirjes @>. E përdora këtë në një test të ri dhe pati një ndikim të madh në kohë: 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ë bazës së të dhënave
Le të krahasojmë madhësitë e tabelave për të dyja qasjet. Në psql, ne mund të shfaqim madhësinë e të gjitha tabelave dhe indekseve duke përdorur komandën dti+

Me qasjen EAV, madhësitë e tabelave janë afërsisht 3068 MB dhe indekset janë deri në 3427 MB, për një total prej 6,43 GB. Duke përdorur qasjen JSONB, tabela përdor 1817 MB dhe indekset 318 MB, për një total prej 2,08 GB. Kjo është një e treta e madhësisë! Ky fakt më habiti pak, pasi ne ruajmë emrat e vetive në çdo objekt JSONB.
Por numrat flasin vetë: në EAV, ne ruajmë dy çelësa të huaj me numra të plotë për vlerën e atributit, duke rezultuar në 8 bajt të dhëna shtesë. Për më tepër, në EAV, të gjitha vlerat e vetive ruhen si tekst, ndërsa JSONB do të përdorë vlera numerike dhe logjike brenda vendit, aty ku është e mundur, duke rezultuar në një gjurmë më të vogël.
Rezultatet e
Në përgjithësi, mendoj se ruajtja e vetive të entitetit në formatin JSONB mund ta thjeshtojë ndjeshëm projektimin dhe mirëmbajtjen e bazës suaj të të dhënave. Nëse kryeni shumë pyetje, ruajtja e gjithçkaje në të njëjtën tabelë me entitetin do të jetë vërtet më efikase. Fakti që thjeshton ndërveprimet e të dhënave është tashmë një plus, por baza e të dhënave që rezulton është gjithashtu tre herë më e vogël në madhësi.
Gjithashtu, bazuar në rezultatet e testit benchmark, mund të konkludojmë se penalizimi i performancës është shumë i vogël. Në disa raste, JSONB madje performon më shpejt se EAV, duke e bërë atë edhe më të mirë. Megjithatë, ky test benchmark sigurisht që nuk i mbulon të gjitha aspektet (p.sh., entitete me një numër shumë të madh vetish, një rritje të konsiderueshme të numrit të vetive në të dhënat ekzistuese, etj.), kështu që nëse keni ndonjë sugjerim për përmirësim, ju lutemi mos ngurroni t'i lini ato në komente!
Burimi: www.habr.com
