EAV:n korvaaminen JSONB:llä PostgreSQL:ssä

TL; DR: JSONB voi yksinkertaistaa tietokantaskeeman kehitystä huomattavasti kyselyn suorituskyvystä tinkimättä.

Esittely

Otetaan klassinen esimerkki relaatiotietokannan (tietokannan) luultavasti yhdestä maailman vanhimmista käyttötapauksista: meillä on entiteetti, ja meidän on tallennettava tämän entiteetin tietyt ominaisuudet (attribuutit). Mutta kaikilla esiintymillä ei välttämättä ole samoja ominaisuuksia, ja uusia ominaisuuksia voidaan lisätä tulevaisuudessa.

Helpoin tapa ratkaista tämä ongelma on luoda tietokantataulukkoon sarake kullekin ominaisuuden arvolle ja yksinkertaisesti täyttää ne, jotka tarvitaan tietylle entiteettiinstanssille. Loistava! Ongelma ratkaistu... kunnes taulukossasi on miljoonia tietueita ja sinun on lisättävä uusi tietue.

Harkitse EAV-kuviota (Entiteetti-attribuutti-arvo), sitä esiintyy melko usein. Yksi taulukko sisältää entiteettejä (tietueita), toinen taulukko sisältää ominaisuuksien nimet (attribuutit), ja kolmas taulukko liittää entiteetit niiden attribuutteihin ja sisältää näiden attribuuttien arvon nykyiselle entiteetille. Tämä antaa sinulle mahdollisuuden saada erilaisia ​​ominaisuusjoukkoja eri objekteille ja myös lisätä ominaisuuksia lennossa muuttamatta tietokannan rakennetta.

En kuitenkaan kirjoittaisi tätä viestiä, ellei EVA-lähestymistavalla olisi joitain huonoja puolia. Joten esimerkiksi yhden tai useamman entiteetin saamiseksi, joilla kullakin on 1 attribuutti, kyselyssä tarvitaan 2 liitosta: ensimmäinen on liitos attribuuttitaulukon kanssa, toinen on liitos arvotaulukon kanssa. Jos entiteetillä on 2 attribuuttia, tarvitaan 4 liittämistä! Lisäksi kaikki attribuutit tallennetaan tyypillisesti merkkijonoina, mikä johtaa sekä tuloksen että WHERE-lauseen tyyppivaltaan. Jos kirjoitat paljon kyselyitä, tämä on melko turhaa resurssien käytön kannalta.

Näistä ilmeisistä puutteista huolimatta EAV:ta on käytetty pitkään tällaisten ongelmien ratkaisemiseen. Nämä olivat väistämättömiä puutteita, eikä parempaa vaihtoehtoa ollut.
Mutta sitten uusi "teknologia" ilmestyi PostgreSQL: hen...

PostgreSQL 9.4:stä alkaen JSONB-tietotyyppi lisättiin JSON-binääritietojen tallentamiseen. Vaikka JSONin tallentaminen tässä muodossa vie tavallisesti hieman enemmän tilaa ja aikaa kuin pelkän tekstin JSON, toimintojen suorittaminen sillä on paljon nopeampaa. JSONB tukee myös indeksointia, mikä tekee kyselyistä entistä nopeampia.

JSONB-tietotyypin avulla voimme korvata hankalan EAV-kuvion lisäämällä vain yhden JSONB-sarakkeen entiteettitaulukkoomme, mikä yksinkertaistaa huomattavasti tietokannan suunnittelua. Mutta monet väittävät, että tähän pitäisi liittyä tuottavuuden lasku... Siksi kirjoitin tämän artikkelin.

Testitietokannan perustaminen

Tätä vertailua varten loin tietokannan PostgreSQL 9.5:n tuoreelle asennukselle 80 dollarin koontiversiolle DigitalOcean Ubuntu 14.04. Asetettuani joitain parametreja postgresql.confissa juoksin tämä komentosarja psql:llä. Seuraavat taulukot luotiin tietojen esittämiseksi EAV-muodossa:

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

Alla on taulukko, johon samat tiedot tallennetaan, mutta attribuutit JSONB-tyyppisessä sarakkeessa - ominaisuudet.

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

Näyttää paljon yksinkertaisemmalta, eikö? Sitten se lisättiin entiteettitaulukoihin (kokonaisuus & entity_jsonb) 10 miljoonaa tietuetta, ja vastaavasti taulukko täytettiin samoilla tiedoilla käyttämällä EAV-mallia ja lähestymistapaa JSONB-sarakkeella - entity_jsonb.properties. Näin ollen saimme useita eri tietotyyppejä koko kiinteistöjoukosta. Esimerkkitiedot:

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

Joten nyt meillä on samat tiedot molemmista vaihtoehdoista. Aloitetaan toteutusten vertailu työssä!

Yksinkertaista suunnitteluasi

Aiemmin kerrottiin, että tietokannan suunnittelua yksinkertaistettiin huomattavasti: yksi taulukko käyttämällä JSONB-saraketta ominaisuuksille sen sijaan, että olisi käytetty kolme taulukkoa EAV:lle. Mutta miten tämä näkyy pyynnöissä? Yhden kokonaisuuden omaisuuden päivittäminen näyttää tältä:

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

Kuten näette, viimeinen pyyntö ei näytä yksinkertaisemmalta. JSONB-objektin ominaisuuden arvon päivittämiseksi meidän on käytettävä toimintoa jsonb_set(), ja sen pitäisi välittää uusi arvomme JSONB-objektina. Meidän ei kuitenkaan tarvitse tietää mitään tunnistetta etukäteen. Tarkasteltaessa EAV-esimerkkiä meidän on tiedettävä sekä entity_id että entity_attribute_id päivityksen suorittamiseksi. Jos haluat päivittää JSONB-sarakkeen ominaisuuden kohteen nimen perusteella, kaikki tehdään yhdellä yksinkertaisella rivillä.

Valitse nyt juuri päivittämämme entiteetti sen uuden värin perusteella:

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

Uskon, että voimme olla samaa mieltä siitä, että toinen on lyhyempi (ei liittymistä!) ja siksi luettavampi. JSONB voittaa täällä! Käytämme JSON ->> -operaattoria saadaksemme värin tekstiarvona JSONB-objektista. On myös toinen tapa saavuttaa sama tulos JSONB-mallissa käyttämällä @>-operaattoria:

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

Tämä on hieman monimutkaisempi: tarkistamme, sisältääkö ominaisuudet-sarakkeen JSON-objekti objektin, joka on @>-operaattorin oikealla puolella. Vähemmän luettava, tuottavampi (katso alla).

Tehdään JSONB:n käytöstä entistä helpompaa, kun sinun on valittava useita ominaisuuksia kerralla. Tässä JSONB-lähestymistapa todella tulee esiin: valitsemme yksinkertaisesti ominaisuudet lisäsarakkeiksi tulosjoukostamme ilman liitoksia:

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

EAV:n kanssa tarvitset 2 liitosta jokaista kohdetta kohden. Mielestäni yllä olevat kyselyt osoittavat suurta yksinkertaistamista tietokannan suunnittelussa. Katso lisää esimerkkejä JSONB-kyselyjen kirjoittamisesta, myös sisään tämä lähettää.
Nyt on aika puhua suorituskyvystä.

Suorituskyky

Vertailemaan suorituskykyä käytin SELITYS ANALYSOINTI kyselyissä suoritusajan laskemiseksi. Jokainen kysely suoritettiin vähintään kolme kertaa, koska kyselyn suunnittelija kestää kauemmin ensimmäisellä kerralla. Ensin suoritin kyselyt ilman indeksejä. Ilmeisesti tämä oli JSONB:n etu, koska EAV:n edellyttämät liitokset eivät voineet käyttää indeksejä (vieraat avainkentät eivät olleet indeksoituja). Tämän jälkeen loin indeksin EAV-arvotaulukon 2 vierasavainsarakkeeseen sekä indeksin GIN JSONB-sarakkeelle.

Tietojen päivitys näytti seuraavat tulokset ajassa (ms). Huomaa, että asteikko on logaritminen:

EAV:n korvaaminen JSONB:llä PostgreSQL:ssä

Näemme, että JSONB on paljon (> 50000-x) nopeampi kuin EAV, jos et käytä indeksejä yllä mainitusta syystä. Kun indeksoimme sarakkeita ensisijaisilla avaimilla, ero lähes häviää, mutta JSONB on silti 1,3 kertaa nopeampi kuin EAV. Huomaa, että JSONB-sarakkeen indeksillä ei ole vaikutusta tähän, koska emme käytä ominaisuussaraketta arviointiehdoissa.

Kun valitsemme tiedot kiinteistön arvon perusteella, saamme seuraavat tulokset (normaali mittakaava):

EAV:n korvaaminen JSONB:llä PostgreSQL:ssä

Voit huomata, että JSONB toimii taas nopeammin kuin EAV ilman indeksejä, mutta kun EAV indekseillä, se toimii silti nopeammin kuin JSONB. Mutta sitten huomasin, että JSONB-kyselyiden ajat olivat samat, mikä sai minut toteamaan, että GIN-indeksit eivät toimi. Ilmeisesti kun käytät GIN-indeksiä sarakkeessa, jossa on täytetyt ominaisuudet, se tulee voimaan vain käytettäessä include-operaattoria @>. Käytin tätä uudessa testissä ja sillä oli valtava vaikutus aikaan: vain 0,153 ms! Tämä on 15000 25000 kertaa nopeampi kuin EAV ja XNUMX XNUMX kertaa nopeampi kuin ->> -operaattori.

Mielestäni se oli tarpeeksi nopea!

Tietokantataulukon koko

Verrataan molempien lähestymistapojen taulukkokokoja. Psql:ssä voimme näyttää kaikkien taulukoiden ja indeksien koon komennolla dti+

EAV:n korvaaminen JSONB:llä PostgreSQL:ssä

EAV-lähestymistavassa taulukoiden koot ovat noin 3068 Mt ja indeksit jopa 3427 Mt, yhteensä 6,43 Gt. JSONB-lähestymistapa käyttää 1817 Mt taulukkoon ja 318 Mt indekseihin, mikä on 2,08 Gt. Se osoittautuu 3 kertaa vähemmän! Tämä seikka yllätti minut hieman, koska tallennamme ominaisuuksien nimet jokaiseen JSONB-objektiin.

Mutta silti, luvut puhuvat puolestaan: EAV:ssa tallennamme 2 kokonaislukua vieraa-avainta attribuutin arvoa kohden, jolloin saadaan 8 tavua lisätietoa. Lisäksi EAV tallentaa kaikki ominaisuusarvot tekstinä, kun taas JSONB käyttää numeerisia ja boolean-arvoja sisäisesti mahdollisuuksien mukaan, mikä johtaa pienempään jalanjälkeen.

Tulokset

Kaiken kaikkiaan mielestäni kokonaisuuden ominaisuuksien tallentaminen JSONB-muodossa voi tehdä tietokannan suunnittelusta ja ylläpidosta paljon helpompaa. Jos suoritat paljon kyselyjä, kaiken pitäminen samassa taulukossa entiteetin kanssa toimii itse asiassa tehokkaammin. Ja se, että tämä yksinkertaistaa tietojen välistä vuorovaikutusta, on jo plussaa, mutta tuloksena oleva tietokanta on 3 kertaa pienempi.

Myös suoritettujen testien perusteella voimme päätellä, että suorituskykyhäviöt ovat erittäin merkityksettömiä. Joissakin tapauksissa JSONB on jopa nopeampi kuin EAV, mikä tekee siitä vielä paremman. Tämä vertailuarvo ei tietenkään kata kaikkia näkökohtia (esim. entiteetit, joilla on erittäin suuri määrä ominaisuuksia, olemassa olevan datan ominaisuuksien lukumäärän merkittävä kasvu,...), joten jos sinulla on ehdotuksia niiden parantamiseksi , jätä rohkeasti kommentteihin!

Lähde: will.com

Lisää kommentti