Skipt um EAV fyrir JSONB í PostgreSQL

TL;DR: JSONB getur einfaldað þróun gagnagrunnsskema til muna án þess að fórna afköstum fyrirspurna.

Inngangur

Tökum klassískt dæmi, líklega eitt elsta notkunartilvikið í heimi gagnagrunna með tengslum: við höfum einingu og þurfum að geyma ákveðna eiginleika (attribute) þessarar einingar. En ekki öll tilvik kunna að hafa sama safn eiginleika og fleiri eiginleikar gætu verið bætt við í framtíðinni.

Einfaldasta lausnin á þessu vandamáli er að búa til dálk í gagnagrunnstöflunni fyrir hvert eiginleikagildi og einfaldlega fylla út þá dálka sem þarf fyrir tiltekið einingartilvik. Frábært! Vandamálið leyst... þangað til taflan þín inniheldur milljónir færslna og þú þarft að bæta við nýrri færslu.

Við skulum skoða EAV-mynstrið (Einingar-eiginleika-gildi), það er frekar algengt. Ein tafla inniheldur einingar (færslur), önnur tafla inniheldur eiginleikaheiti (eigindir) og þriðja taflan tengir einingar við eiginleika þeirra og inniheldur gildi þessara eiginleika fyrir núverandi einingu. Þetta gerir þér kleift að hafa mismunandi eiginleikasett fyrir mismunandi hluti, sem og bæta við eiginleikum samstundis, án þess að breyta gagnagrunnsbyggingunni.

Ég væri þó ekki að skrifa þessa færslu ef það væru ekki nokkrir gallar við EVA aðferðina. Til dæmis, að sækja eina eða fleiri einingar með einum eigindi hver krefst tveggja tenginga í fyrirspurninni: fyrsta tengingin við eigindatöfluna, önnur tengingin við gildistöfluna. Ef eining hefur tvo eiginleika, þá þarf fjóra tenginga! Ennfremur eru allir eiginleikar venjulega geymdir sem strengir, sem leiðir til tegundarþvingunar fyrir bæði niðurstöðuna og WHERE klausuna. Ef þú skrifar margar fyrirspurnir, þá er þetta frekar sóun hvað varðar notkun auðlinda.

Þrátt fyrir þessa augljósu galla hefur EAV lengi verið notað til að leysa þess konar vandamál. Þetta voru óhjákvæmilegir gallar og það var einfaldlega enginn betri kostur í boði.
En þá birtist ný „tækni“ í PostgreSQL…

Frá og með PostgreSQL 9.4 var gagnategundinni JSONB bætt við til að geyma tvíundar JSON gögn. Þó að geymsla JSON á þessu sniði taki venjulega aðeins meira pláss og tíma en venjulegt JSON, eru aðgerðir með því mun hraðari. JSONB styður einnig flokkun, sem gerir fyrirspurnir enn hraðari.

JSONB gagnategundin gerir okkur kleift að skipta út fyrirferðarmiklu EAV mynstrinu með því að bæta aðeins einum JSONB dálki við einingartöfluna okkar, sem einfaldar hönnun gagnagrunnsins verulega. Hins vegar halda margir því fram að þetta kosti afköst... Þess vegna skrifaði ég þessa grein.

Að setja upp prófunargagnagrunn

Til að bera saman þennan gagnagrunn bjó ég til með nýrri uppsetningu af PostgreSQL 9.5 í $80 útgáfunni. DigitalOcean Ubuntu 14.04 Eftir að hafa stillt nokkrar breytur í postgresql.conf keyrði ég þetta handrit með psql. Til að birta gögnin sem EAV voru eftirfarandi töflur búnar til:

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

Hér að neðan er tafla þar sem sömu gögn verða geymd, en með eigindum í JSONB dálki – eignir.

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

Lítur miklu einfaldara út, er það ekki? Síðan var því bætt við einingartöflurnar (einingin & entity_jsonb) 10 milljónir færslna og þar af leiðandi var taflan fyllt með eins gögnum þar sem EAV-mynstrið og aðferðin með JSONB-dálknum voru notuð – entity_jsonb.propertiesÞannig fengum við nokkrar mismunandi gagnategundir yfir allt safn eiginleika. Dæmi um gögn:

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

Nú höfum við eins gögn fyrir báða valkostina. Byrjum að bera saman útfærslurnar í raunveruleikanum!

Einföldun hönnunarinnar

Áður var minnst á að hönnun gagnagrunnsins var verulega einfölduð: ein tafla, með JSONB dálki fyrir eiginleika, í stað þriggja taflna fyrir EAV. En hvernig þýðir þetta yfir í fyrirspurnir? Uppfærsla á einum eiginleika lítur svona út:

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

Eins og þú sérð lítur síðasta fyrirspurnin ekki einfaldari út. Til að uppfæra gildi eiginleika í JSONB hlut verðum við að nota fallið jsonb_set(), og verður að senda nýja gildið okkar sem JSONB hlut. Hins vegar þurfum við ekki að vita neitt auðkenni fyrirfram. Ef við skoðum EAV dæmið þurfum við að vita bæði entity_id og entity_attribute_id til að framkvæma uppfærsluna. Ef þú vilt uppfæra eiginleika í JSONB dálki út frá hlutarheitinu, þá er það allt gert í einni einfaldri línu.

Nú skulum við velja eininguna sem við uppfærðum út frá nýja litnum:

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

Ég held að við getum verið sammála um að seinni útgáfan sé styttri (án sameiningarinnar!) og því læsilegri. JSONB vinnur hér! Við notum JSON ->> virkjann til að sækja litinn sem textagildi úr JSONB hlutnum. Það er líka önnur leið til að ná sömu niðurstöðu í JSONB líkaninu með því að nota @> virkjann:

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

Þetta er aðeins flóknara: við athugum hvort JSON hluturinn í eiginleikadálknum inniheldur hlutinn hægra megin við @> virkjann. Ólæsilegra, afkastameira (sjá hér að neðan).

Einfaldum notkun JSONB enn frekar þegar þú þarft að velja marga eiginleika í einu. Þetta er þar sem JSONB aðferðin skín virkilega: við veljum einfaldlega eiginleika sem viðbótar dálka í niðurstöðumenginu okkar, án þess að þurfa að nota tengingar:

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

Með EAV þarftu tvær tengingar fyrir hverja eiginleika sem þú vilt leita í. Að mínu mati sýna fyrirspurnirnar hér að ofan verulega einföldun í gagnagrunnshönnun. Þú getur einnig séð fleiri dæmi um hvernig á að skrifa JSONB fyrirspurnir í þetta staða.
Nú er kominn tími til að tala um frammistöðu.

Framleiðni

Til að bera saman frammistöðuna sem ég notaði ÚTSKÝRA GREINA í fyrirspurnum til að reikna út keyrslutíma. Hver fyrirspurn var keyrð að minnsta kosti þrisvar sinnum, því fyrirspurnaráætlunin tekur lengri tíma í fyrsta skipti. Fyrst keyrði ég fyrirspurnirnar án nokkurra vísitölna. Þetta var augljóslega kostur við JSONB, þar sem tengingarnar sem krafist var fyrir EAV gátu ekki notað vísitölur (erlendir lykilreitir voru ekki vísitöluð). Eftir það bjó ég til vísitölu á tveimur erlendum lykildálkum EAV gildistöflunnar, sem og vísitölu. GIN fyrir JSONB dálkinn.

Gagnauppfærslur sýndu eftirfarandi tímaniðurstöður (í ms). Athugið að kvarðinn er lógaritmískur:

Skipt um EAV fyrir JSONB í PostgreSQL

Við sjáum að JSONB er marktækt (>50000x) hraðara en EAV án vísitölna, af þeirri ástæðu sem nefnd var hér að ofan. Þegar við vísum í dálka með aðallyklum hverfur munurinn næstum því, en JSONB er samt 1,3x hraðara en EAV. Athugið að vísitalan í JSONB dálknum hefur engin áhrif hér, þar sem við notum ekki eiginleikadálkinn í matsviðmiðunum.

Fyrir gagnaval byggt á fasteignavirði fáum við eftirfarandi niðurstöður (venjulegur kvarði):

Skipt um EAV fyrir JSONB í PostgreSQL

Þú sérð að JSONB er aftur hraðara en EAV án vísitölna, en þegar EAV er vísitölubundið er það samt hraðara en JSONB. En þá sá ég að tíminn fyrir JSONB fyrirspurnirnar var sá sami, sem leiddi mig að þeirri staðreynd að GIN vísitölur eru ekki virkjaðar. Greinilega, þegar þú notar GIN vísitölu á dálki með útfylltum eiginleikum, þá tekur hún aðeins gildi þegar @> inclusion virknin er notuð. Ég notaði þetta í nýrri prófun og það hafði gríðarleg áhrif á tímann: aðeins 0,153 ms! Það er 15000 sinnum hraðara en EAV og 25000 sinnum hraðara en ->> virknin.

Mér fannst þetta vera ansi hratt!

Stærð gagnagrunnstöflu

Við skulum bera saman stærðir töflu fyrir báðar aðferðirnar. Í psql getum við birt stærð allra taflna og vísitölna með skipuninni dti+

Skipt um EAV fyrir JSONB í PostgreSQL

Með EAV aðferðinni eru töflustærðir um það bil 3068 MB og vísitölur allt að 3427 MB, sem er samtals 6,43 GB. Með JSONB aðferðinni notar taflan 1817 MB og vísitölurnar eru 318 MB, sem er samtals 2,08 GB. Það er þriðjungur af stærðinni! Þessi staðreynd kom mér svolítið á óvart, þar sem við geymum eiginleikaheiti í hverjum JSONB hlut.

En tölurnar tala sínu máli: í EAV geymum við tvo heiltölu erlenda lykla fyrir hvert eigindagildi, sem leiðir til 8 bæti af viðbótargögnum. Ennfremur, í EAV, eru öll eiginleikagildi geymd sem texti, en JSONB notar töluleg og rökrétt gildi innbyrðis þar sem það er mögulegt, sem leiðir til minni notkunar.

Niðurstöður

Ég tel að það að geyma eiginleika eininga á JSONB sniði geti einfaldað hönnun og viðhald gagnagrunnsins verulega. Ef þú framkvæmir margar fyrirspurnir verður það sannarlega skilvirkara að geyma allt í sömu töflu og einingin. Sú staðreynd að það einfaldar gagnvirkni er þegar kostur, en gagnagrunnurinn sem myndast er líka þrisvar sinnum minni að stærð.

Einnig, byggt á niðurstöðum viðmiðunar, getum við ályktað að afköstin séu mjög lítil. Í sumum tilfellum virkar JSONB jafnvel hraðar en EAV, sem gerir það enn betra. Hins vegar nær þetta viðmið alls ekki yfir alla þætti (t.d. einingar með mjög mikinn fjölda eiginleika, verulega aukningu á fjölda eiginleika í núverandi gögnum o.s.frv.), svo ef þú hefur einhverjar tillögur að úrbótum, vinsamlegast skildu þær eftir í athugasemdunum!

Heimild: www.habr.com

Kauptu áreiðanlega hýsingu fyrir síður með DDoS vernd, VPS VDS netþjónum 🔥 Kauptu áreiðanlega vefhýsingu með DDoS vörn, VPS VDS netþjónum | ProHoster