EAV asendamine JSONB-ga PostgreSQL-is

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 (Üksus-atribuut-vÀÀrtus), 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 DigitalOcean Ubuntu 14.04 PÀrast postgresql.conf failis mÔnede parameetrite konfigureerimist kÀivitasin see 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 jsonb_set()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 see postitus.
NĂŒĂŒd on aeg rÀÀkida jĂ”udlusest.

ĐŸŃ€ĐŸĐžĐ·ĐČĐŸĐŽĐžŃ‚Đ”Đ»ŃŒĐœĐŸŃŃ‚ŃŒ

Kasutasin jĂ”udluse vĂ”rdlemiseks SELGITAGE ANALÜÜSI 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 GIN JSONB veeru jaoks.

AndmevÀrskendus nÀitas ajaliselt (ms) jÀrgmisi tulemusi. Pange tÀhele, et skaala on logaritmiline:

EAV asendamine JSONB-ga PostgreSQL-is

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

EAV asendamine JSONB-ga PostgreSQL-is

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 asendamine JSONB-ga PostgreSQL-is

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

Ostke DDoS-kaitsega saitide jaoks usaldusvÀÀrne hostimine, VPS VDS-serverid đŸ”„ Osta usaldusvÀÀrne veebimajutus DDoS-kaitsega, VPS VDS serverid | ProHoster