EAV asendamine JSONB-ga PostgreSQL-is

TL; DR: JSONB vÔib oluliselt lihtsustada andmebaasi skeemi arendamist, ilma et see kahjustaks pÀringute jÔudlust.

Sissejuhatus

Toome klassikalise nĂ€ite, mis on tĂ”enĂ€oliselt ĂŒks vanimaid rakendusi relatsiooniliste andmebaaside (andmebaas) maailmas: meil on ĂŒksus ja on vaja salvestada selle ĂŒksuse teatud omadused (atribuutid). Kuid mitte kĂ”ik eksemplarid ei pruugi omada sama omaduste komplekti, lisaks vĂ”ib tulevikus olla vajalik uusi omadusi lisada.

Lihtsaim viis selle probleemi lahendamiseks on luua andmebaasitabelis iga omaduse vÀÀrtuse jaoks veerg ja lihtsalt tĂ€ita need, mis on vajalikud konkreetse ĂŒksuse eksemplari jaoks. SuurepĂ€rane! Probleem on lahendatud
 kuni teie tabelis on miljoneid kirjeid ja teil on vaja lisada uus kirje.

Kaalume EAV mustrit (Entity-Attribute-Value), mida kasutatakse ĂŒsna sageli. Üks tabel sisaldab ĂŒksusi (kirjeid), teine tabel sisaldab omaduste (atribuutide) nimesid ja kolmas tabel seob ĂŒksused nende atribuutidega ning sisaldab nende atribuutide vÀÀrtust kohaliku ĂŒksuse jaoks. See vĂ”imaldab teil omada erinevaid omaduste komplekte erinevate objektide jaoks ja lisada omadusi „reisi jooksul“, muutes andmebaasi struktuuri.

Kuid ma ei kirjutaks seda mĂ€rkust, kui EAV lĂ€henemisel ei oleks puudusi. NĂ€iteks, et saada ĂŒhe vĂ”i mitme ĂŒksuse, millel on 1 atribuut, nĂ”uab see pĂ€ringus 2 join’i: esimene – liitmine atribuutide tabeliga, teine – liitmine vÀÀrtuste tabeliga. Kui ĂŒksusel on 2 atribuuti, on juba vaja 4 join’i! Lisaks hoitakse kĂ”iki atribuute tavaliselt stringidena, mis toob kaasa tĂŒĂŒpide konvertimise nii tulemusele kui ka WHERE tingimusele. Kui kirjutate palju pĂ€ringuid, on see ressursikasutuse seisukohalt ĂŒsna raiskav.

Sellest hoolimata on EAV-i juba ammu kasutatud selliste probleemide lahendamiseks. Need olid vÀltimatud puudused ja paremat alternatiivi polnud lihtsalt olemas.
Aga siis ilmus PostgreSQL-i uus „tehnoloogia“


Alates PostgreSQL 9.4-st on lisatud JSONB andmetĂŒĂŒp JSON-i binaarsete andmete salvestamiseks. Kuigi JSON-i salvestamine selles formaadis vĂ”tab tavaliselt veidi rohkem ruumi ja aega kui lihtne tekstiline JSON, toimub selle töötlemine palju kiiremini. Samuti toetab JSONB indekseerimist, mis muudab selle pĂ€ringud veelgi kiiremaks.

JSONB andmetĂŒĂŒp vĂ”imaldab meil asendada mahuka EAV mustri, lisades vaid ĂŒhe JSONB veeru meie entiteetide tabelisse, mis lihtsustab oluliselt andmebaasi kujundamist. Kuid paljud vĂ€idavad, et see peaks tingima jĂ”udluse languse
 Just sellepĂ€rast ma selle artikli kirjutasin.

Testandmebaasi seadistamine

Selle vĂ”rdluse jaoks lĂ”in andmebaasi uue PostgreSQL 9.5 paigalduse peal 80-dollarilisel sĂŒsteemil DigitalOcean Ubuntu 14.04. PĂ€rast postgresql.conf-i mĂ”ningate seadete seadistamist kĂ€ivitasin see skripti psql abil. EAV kujul andmete esitamiseks loodi 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, kus salvestatakse samad andmed, kuid atribuutide veerg on JSONB tĂŒĂŒpi – properties.

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

NĂ€eb kordades lihtsam vĂ€lja, eks? SeejĂ€rel lisati entiteetide tabelitesse (entity & entity_jsonb) 10 miljonit kirjet ning vastavalt tĂ€ideti samade andmetega tabelid, kus kasutatakse EAV mustrit ja JSONB veeru lĂ€henemist – entity_jsonb.properties. Nii saime erinevaid andmetĂŒĂŒpe kogu atribuudi hulgast. Andmete nĂ€ide:

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

NĂŒĂŒd on meil samad andmed kahe variandi jaoks. Alustame nende rakenduste vĂ”rdlemist!

Kujunduse lihtsustamine

Olen juba varem maininud, et andmebaasi disain on oluliselt lihtsustunud: ĂŒks tabel, tĂ€nu JSONB veeru kasutamisele omaduste jaoks, selle asemel et kasutada kolme tabelit EAV jaoks. Kuidas see aga pĂ€ringutes kajastub? Entiteedi ĂŒhe omaduse 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 me nĂ€eme, ei tundu viimane pĂ€ring lihtsam. Et vĂ€rskendada omaduse vÀÀrtust JSONB objektis, peame kasutama funktsiooni jsonb_set(), ja peame edastama meie uue vÀÀrtuse JSONB objektina. Siiski ei pea me eelnevalt mingit identifikaatorit teadma. EAV nĂ€ite pĂ”hjal peame teadma nii entity_id kui entity_attribute_id, et teostada vĂ€rskendust. Kui soovite vĂ€rskendada omadust JSONB veerus objekti nime pĂ”hjal, – siis toimub see kĂ”ik ĂŒhe lihtsa lausega.

NĂŒĂŒd valime selle entiteedi, mida just vĂ€rskendasime, tema uue vĂ€rvi tingimuse alusel:

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

Mina arvan, et me saame nĂ”ustuda, et teine on lĂŒhem (ilma joinita!), ja seega lugeda kergemini. Siin on JSONB vĂ”it! Kasutame JSON operaatorit ->>, et saada vĂ€rv tekstivÀÀrtusena JSONB objektist. Samuti on olemas teine vĂ”imalus sama tulemuse saavutamiseks JSONB mudelis, kasutades operaatorit @>:

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

See on veidi keerulisem: me kontrollime, kas JSON objekt veerus properties sisaldab objekti, mis on paremal pool operaatorit @>. VĂ€hem loetav, kuid tootlikum (vt hiljem).

Lihtsustame JSONB kasutamist veelgi, kui peate valima korraga mitu omadust. Just siin sobib JSONB lĂ€henemine tĂ”eliselt hĂ€sti: me valime lihtsalt omadused lisakolumnitena meie tulemuste kogumis ilma vajaduseta ĂŒhinemiste jĂ€rele:

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

EAV korral vajate igaks omaduseks, mida soovite kĂŒsida, 2 ĂŒhinemist. Minu arvates nĂ€itavad eeltoodud pĂ€ringud suurt lihtsustamist andmebaasi disainis. Vaadake rohkem nĂ€iteid JSONB pĂ€ringute kirjutamisest ka selles postitusest.
NĂŒĂŒd on aeg rÀÀkida tootlikkusest.

TÔhusus

Tootlikkuse vĂ”rdlemiseks kasutasin EXPLAIN ANALYZE pĂ€ringutes, et kokku lugeda tĂ€itmise aega. Iga pĂ€ring viidi lĂ€bi vĂ€hemalt kolm korda, kuna esmakordne pĂ€ring nĂ”udis pĂ€ringute planeerijalt rohkem aega. Alguses tegin pĂ€ringud ilma mingite indeksiteta. Selgelt oli see JSONB eelise aspekt, kuna EAV jaoks vajalikud ĂŒhendused ei saanud kasutada indekseid (vĂ€listĂ€htede vĂ€lja ei indeksitud). SeejĂ€rel lĂ”in indeksi kahe EAV vÀÀrtuste tabeli vĂ€listĂ€htede veeru jaoks ja ka indeksi GIN JSONB veeru jaoks.

Andmete vÀrskendamine nÀitas jÀrgmisi tulemusi aja osas (ms). Pange tÀhele, et skaala on logaritmiline:

EAV asendamine JSONB-ga PostgreSQL-is

NÀeme, et JSONB on palju (> 50000-kordselt) kiirem kui EAV, kui indekseid ei kasutata, pÔhjuse tÔttu, nagu eespool mainitud. Kui indekseme veergu peavÔtmete jÀrgi, siis vahe peaaegu kaob, kuid JSONB on siiski 1,3 korda kiirem kui EAV. TÀhelepanu, et siin ei mÔjuta JSONB veeru indeks midagi, kuna me ei kasuta omaduste veergu hindamiskriteeriumides.

Omaduse vÀÀrtuse pÔhjal andmete valimiseks saame jÀrgmised tulemused (tavaline skaala):

EAV asendamine JSONB-ga PostgreSQL-is

VĂ”ime mĂ€rgata, et JSONB töötab jĂ€lle kiiremini kui EAV ilma indeksiteta, kuid kui EAV-l on indeksid – töötab see siiski kiiremini kui JSONB. Kuid pĂ€rast nĂ€gin, et JSONB pĂ€ringute aeg oli sama, see juhtis mind jĂ€reldusele, et GIN-indeks ei tööta. Tundub, et kui kasutate GIN-indeksit omadustest tĂ€idetud veeru jaoks, kehtib see ainult @> operaatoreid kasutades. Kasutasin seda uues testis, mis mĂ”jutas aega tohutult: ainult 0,153 ms! See on 15000 korda kiiremini kui EAV ja 25000 korda kiiremini kui ->> operaator.

Arvan, et see oli piisavalt kiire!

Andmebaasi tabelite suurused

VÔrdleme tabelite suurusi mÔlema lÀhenemise korral. psql-is saame kÔikide tabelite ja indeksite suurused Àra nÀidata kÀsuga dti+

EAV asendamine JSONB-ga PostgreSQL-is

EAV lĂ€henemise korral on tabelite suurused umbes 3068 MB ja indeksite suurused kuni 3427 MB, kokku 6,43 GB. JSONB lĂ€henemise korral kasutatakse 1817 MB tabeli jaoks ja 318 MB indeksite jaoks, kokku 2,08 GB. See on kolm korda vĂ€hem! See fakt ĂŒllatas mind veidi, kuna salvestame omaduste nimed igas JSONB objektis.

Kuid numbrid rÀÀgivad enda eest: EAV-s salvestame 2 tÀisarvulist vÀlist vÔtit atribuutvÀÀrtusele, mille tulemusena saame 8 baiti tÀiendavaid andmeid. Lisaks salvestatakse EAV-s kÔik omaduste vÀÀrtused tekstina, samas kui JSONB kasutab numbrilisi ja loogilisi vÀÀrtusi seal, kus see on vÔimalik, mis toob kaasa vÀiksema mahu.

Summary

Üldiselt arvan, et isendite omaduste salvestamine JSONB formaadis vĂ”ib oluliselt lihtsustada teie andmebaasi kavandamist ja hooldamist. Kui teete palju pĂ€ringuid, siis kĂ”ik, mis on salvestatud ĂŒhte tabelisse isendiga, töötab tĂ”eliselt tĂ”husamalt. Ja tĂ”siasi, et see lihtsustab andmete vahelist suhtlemist, on juba pluss, kuid ka tulemuseks olev andmebaas on kolm korda vĂ€iksema mahuga.

Samuti, tehtud testide pÔhjal, vÔib kokkuvÔtteks öelda, et jÔudluskaod on vÀga vÀikesed. MÔnes olukorras töötab JSONB isegi kiiremini kui EAV, mis teeb selle veelgi paremaks. Siiski ei kata see standardtest kindlasti kÔiki aspekte (nÀiteks isendid, millel on vÀga palju omadusi, olulised suurendused olemasolevate andmete omaduste arvus,...), seega, kui teil on ettepanekuid nende parandamiseks, siis Àrge kartke jÀtta kommentaare!

Allikas: habr.com

Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid đŸ”„ Osta usaldusvÀÀrne hostimine veebilehtede jaoks DDoS-i kaitsega, VPS VDS serverid | ProHoster