EAV JSONBrekin ordezkatu PostgreSQL-n

TL; DR: JSONB-k datu-basearen eskemaren garapena asko erraztu dezake kontsultaren errendimendua uko egin gabe.

Sarrera

Eman dezagun seguruenik datu-base erlazional baten (datu-basearen) munduko erabilera kasu zaharrenetako baten adibide klasiko bat: entitate bat dugu, eta entitate horren zenbait propietate (atributu) gorde behar ditugu. Baina baliteke instantzia guztiek ez edukitzea propietate multzo bera, eta baliteke propietate gehiago gehitzea etorkizunean.

Arazo hau konpontzeko modurik errazena datu-base-taulan zutabe bat sortzea da jabetza-balio bakoitzerako, eta besterik gabe, entitate-instantzia zehatz baterako behar direnak betetzea. Bikaina! Arazoa konpondu da... zure taulak milioika erregistro dituen arte eta erregistro berri bat gehitu behar duzun arte.

Demagun EAV eredua (Entitatea-Atributua-Balioa), sarritan gertatzen da. Taula batek entitateak (erregistroak) ditu, beste taula batek propietate-izenak (atributuak) eta hirugarren taula batek entitateak bere atributuekin lotzen ditu eta atributu horien balioa dauka uneko entitatearentzat. Honek objektu ezberdinentzako propietate multzo desberdinak izateko gaitasuna ematen dizu, eta, gainera, propietateak berehala gehitzeko datu-basearen egitura aldatu gabe.

Hala ere, ez nuke mezu hau idatziko EVAren planteamenduaren alde txarrak egongo ez balira. Beraz, adibidez, bakoitzak atributu 1 duten entitate bat edo gehiago lortzeko, 2 elkartze behar dira kontsultan: lehenengoa atributu-taularekin bateratze bat da, bigarrena balio-taularekin bateratze bat da. Entitate batek 2 atributu baditu, 4 elkartze behar dira! Gainera, atributu guztiak kate gisa gordetzen dira normalean, eta horren ondorioz, emaitza eta WHERE klausula motak igortzen dira. Kontsulta asko idazten badituzu, hori nahiko alferrikakoa da baliabideen erabilerari dagokionez.

Gabezia nabari horiek gorabehera, EAV aspaldidanik erabiltzen da arazo mota hauek konpontzeko. Hauek ezinbesteko gabeziak ziren, eta ez zegoen alternatiba hoberik.
Baina gero "teknologia" berri bat agertu zen PostgreSQL-en...

PostgreSQL 9.4-tik hasita, JSONB datu-mota gehitu zen JSON datu bitarrak gordetzeko. JSON formatu honetan gordetzeak normalean testu arrunteko JSON baino espazio eta denbora pixka bat gehiago hartzen badu ere, eragiketak egitea askoz azkarragoa da. JSONB-k indexatzea ere onartzen du, eta horrek are azkarrago egiten ditu kontsultak.

JSONB datu-motak EAV eredu astuna ordezkatzeko aukera ematen digu JSONB zutabe bakarra gehituz gure entitateen taulan, datu-basearen diseinua asko erraztuz. Baina askoren ustez, horrek produktibitatearen beherakada izan beharko luke... Horregatik idatzi dut artikulu hau.

Proba datu-base bat konfiguratzea

Konparazio honetarako, PostgreSQL 9.5-en instalazio berri batean sortu nuen datu-basea 80 $ eraikitzean. DigitalOcean Ubuntu 14.04. Postgresql.conf-en parametro batzuk ezarri ondoren exekutatu nuen hau script-a psql erabiliz. Datuak EAV moduan aurkezteko taula hauek sortu dira:

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

Jarraian datu berdinak gordeko diren taula bat dago, baina JSONB motako zutabe batean atributuak dituena - propietate.

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

Askoz sinpleagoa dirudi, ezta? Ondoren, entitateen tauletan gehitu zen (Erakunde & entity_jsonb) 10 milioi erregistro, eta horren arabera, taula datu berdinekin bete zen EAV eredua eta JSONB zutabe batekin hurbilketa erabiliz - entity_jsonb.properties. Horrela, hainbat datu mota jaso ditugu propietate multzo osoaren artean. Datu laginak:

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

Beraz, orain datu berdinak ditugu bi aukeretarako. Has gaitezen inplementazioak konparatzen lanean!

Sinplifikatu zure diseinua

Aurretik esan zen datu-basearen diseinua oso sinplifikatu zela: taula bat, propietateetarako JSONB zutabea erabiliz, EAVrako hiru taula erabili beharrean. Baina nola islatzen da hori eskaeretan? Entitate baten propietatea eguneratzeak honelako itxura du:

-- 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;

Ikus dezakezunez, azken eskaerak ez dirudi sinpleagoa. JSONB objektu bateko propietate baten balioa eguneratzeko funtzioa erabili behar dugu jsonb_set(), eta gure balio berria JSONB objektu gisa pasatu beharko luke. Hala ere, ez dugu aldez aurretik identifikatzailerik ezagutu behar. EAV adibideari erreparatuta, entity_id eta entity_attribute_id ezagutu behar ditugu eguneraketa egiteko. Objektuaren izenaren arabera JSONB zutabe bateko propietate bat eguneratu nahi baduzu, orduan guztia lerro sinple batean egingo da.

Orain hauta dezagun eguneratu berri dugun entitatea bere kolore berriaren arabera:

-- 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';

Uste dut ados gaitezkeela bigarrena laburragoa dela (jotzerik ez!), eta, beraz, irakurgarriagoa dela. JSONB-k irabazten du hemen! JSON ->> eragilea erabiltzen dugu kolorea JSONB objektuaren testu-balio gisa lortzeko. JSONB ereduan emaitza bera lortzeko bigarren modu bat ere badago @> eragilea erabiliz:

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

Hau apur bat konplikatuagoa da: bere propietateen zutabean JSON objektuak @> operadorearen eskuinean dagoen objekturen bat duen egiaztatzen dugu. Gutxiago irakurgarria, produktiboagoa (ikus behean).

Egin dezagun JSONB erabiltzea are errazagoa den hainbat propietate aldi berean hautatu behar dituzunean. Hona hemen JSONB ikuspegia benetan sartzen da: propietateak zutabe gehigarri gisa hautatzen ditugu gure emaitza multzoan batu beharrik gabe:

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

EAV-rekin 2 batu beharko dituzu kontsultatu nahi duzun jabetza bakoitzeko. Nire ustez, goiko kontsultek sinplifikazio handia erakusten dute datu baseen diseinuan. Ikusi JSONB kontsultak idazteko adibide gehiago hemen ere hau mezua.
Orain errendimenduaz hitz egiteko garaia da.

produktibitatea

Erabili nuen errendimendua alderatzeko AZALDU AZALDU kontsultetan, exekuzio denbora kalkulatzeko. Kontsulta bakoitza gutxienez hiru aldiz exekutatu da, kontsulta-planifikatzaileak denbora gehiago behar duelako lehen aldiz. Lehenik eta behin, kontsultak egin nituen indizerik gabe. Hori, jakina, JSONBren abantaila bat izan zen, EAVrako beharrezkoak diren elkartzeek ezin baitzituzten indizeak erabili (gako atzerriko eremuak ez ziren indexatu). Horren ondoren, indize bat sortu nuen EAV balio-taularen 2 atzerriko gako zutabetan, baita indize bat ere. GIN JSONB zutabe baterako.

Datuen eguneraketak denborari dagokionez ondorengo emaitzak erakutsi ditu (ms-tan). Kontuan izan eskala logaritmikoa dela:

EAV JSONBrekin ordezkatu PostgreSQL-n

Ikusten dugu JSONB EAV baino askoz (> 50000-x) azkarragoa dela indizeak erabiltzen ez badituzu, goian adierazitako arrazoiagatik. Lehen gakoekin zutabeak indexatzen ditugunean, aldea ia desagertzen da, baina JSONB EAV baino 1,3 aldiz azkarragoa da oraindik. Kontuan izan JSONB zutabeko indizeak ez duela eraginik hemen, ebaluazio-irizpideetan propietate-zutabea erabiltzen ez dugulako.

Jabetza-balioan oinarritutako datuak hautatzeko, emaitza hauek lortzen ditugu (eskala normala):

EAV JSONBrekin ordezkatu PostgreSQL-n

Konturatuko zara berriro JSONB-k EAV baino azkarrago funtzionatzen duela indizerik gabe, baina EAV indizeekin denean, oraindik ere JSONB baino azkarrago funtzionatzen du. Baina gero ikusi nuen JSONB kontsulten denborak berdinak zirela, horrek bultzatu ninduen GIN indizeek ez dutela funtzionatzen. Antza denez, betetako propietateak dituen zutabe batean GIN indizea erabiltzen duzunean, @> include operadorea erabiltzen denean bakarrik hartzen du eragina. Hau proba berri batean erabili nuen eta denboran eragin handia izan zuen: 0,153 ms bakarrik! Hau EAV baino 15000 aldiz azkarragoa da eta ->> operadorea baino 25000 aldiz azkarragoa.

Nahikoa azkarra izan zela uste dut!

Datu-basearen taularen tamaina

Konpara ditzagun bi ikuspegietarako taularen tamainak. psql-en taula eta indize guztien tamaina erakutsi dezakegu komandoa erabiliz dti+

EAV JSONBrekin ordezkatu PostgreSQL-n

EAV ikuspegirako, taularen tamainak 3068 MB ingurukoak dira eta 3427 MB arteko indizeak 6,43 GB guztira. JSONB ikuspegiak 1817 MB erabiltzen ditu taularako eta 318 MB indizeetarako, hau da, 2,08 GB. 3 aldiz gutxiago ateratzen da! Gertaera honek apur bat harritu nau JSONB objektu guztietan jabetza-izenak gordetzen ditugulako.

Baina, hala ere, zenbakiek beren kabuz hitz egiten dute: EAVn 2 osoko atzerriko gako gordetzen ditugu atributu-balio bakoitzeko, eta ondorioz 8 byte datu gehigarri sortzen dira. Gainera, EAV-k propietate-balio guztiak testu gisa gordetzen ditu, eta JSONB-k, berriz, zenbakizko eta boolear balioak erabiliko ditu barnean, ahal den neurrian, aztarna txikiagoa lortuz.

Emaitzak

Orokorrean, entitateen propietateak JSONB formatuan gordetzeak zure datu-basea diseinatzea eta mantentzea askoz erraztu dezakeela uste dut. Kontsulta asko exekutatzen ari bazara, dena entitatearen taula berean mantentzeak eraginkorrago funtzionatuko du. Eta horrek datuen arteko elkarrekintza errazten duela jada abantaila bat da, baina ondoriozko datu-basea bolumen 3 aldiz txikiagoa da.

Era berean, egindako probetan oinarrituta, errendimendu-galerak oso hutsalak direla ondoriozta dezakegu. Zenbait kasutan, JSONB EAV baino azkarragoa da, are hobea da. Hala ere, erreferente honek, noski, ez ditu alderdi guztiak hartzen (adibidez, propietate-kopuru oso handia duten entitateak, dauden datuen propietate-kopuruaren igoera nabarmena,...), beraz, horiek hobetzeko iradokizunik baduzu. , mesedez utzi lasai iruzkinetan!

Iturria: www.habr.com

Gehitu iruzkin berria