Ranplase EAV ak JSONB nan PostgreSQL

TL; DR: JSONB ka anpil senplifye devlopman chema baz done san sakrifye pèfòmans rechèch.

Entwodiksyon

Ann bay yon egzanp klasik pwobableman youn nan ka yo itilize pi ansyen nan mond lan nan yon baz done relasyon (baz done): nou gen yon antite, epi nou bezwen sove sèten pwopriyete (atribi) nan antite sa a. Men, se pa tout ka gen menm seri pwopriyete yo, epi plis pwopriyete yo ka ajoute nan tan kap vini an.

Fason ki pi fasil pou rezoud pwoblèm sa a se kreye yon kolòn nan tab baz done a pou chak valè pwopriyete, epi tou senpleman ranpli sa yo ki nesesè pou yon egzanp espesifik antite. Gwo! Pwoblèm rezoud... jiskaske tab ou a gen plizyè milyon dosye epi ou bezwen ajoute yon nouvo dosye.

Konsidere modèl EAV (Antite-Atribi-Valè), li rive byen souvan. Yon tab gen antite (dosye), yon lòt tab gen non pwopriyete (atribi), ak yon twazyèm tab asosye antite ak atribi yo epi li gen valè atribi sa yo pou antite aktyèl la. Sa a ba ou kapasite pou gen diferan seri pwopriyete pou objè diferan, epi tou ajoute pwopriyete sou vole san yo pa chanje estrikti baz done a.

Sepandan, mwen pa ta dwe ekri pòs sa a si pa ta gen kèk enkonvenyans nan apwòch la EVA. Se konsa, pou egzanp, pou jwenn youn oswa plizyè antite ki gen 1 atribi chak, 2 rantre yo obligatwa nan rechèch la: premye a se yon rantre ak tab la atribi, dezyèm lan se yon rantre ak tab la valè. Si yon antite gen 2 atribi, Lè sa a, 4 rantre yo nesesè! Anplis de sa, tout atribi yo anjeneral estoke kòm fisèl, ki rezilta nan depoze kalite pou tou de rezilta a ak kloz la WHERE. Si ou ekri yon anpil nan demann, Lè sa a, sa a se byen gaspiye an tèm de itilizasyon resous.

Malgre enpèfeksyon evidan sa yo, EAV te itilize depi lontan pou rezoud pwoblèm sa yo. Sa yo te enpèfeksyon inevitab, e pa te gen tou senpleman pa gen pi bon altènatif.
Men, Lè sa a, yon nouvo "teknoloji" parèt nan PostgreSQL ...

Kòmanse ak PostgreSQL 9.4, yo te ajoute kalite done JSONB nan magazen done binè JSON. Malgre ke estoke JSON nan fòma sa a anjeneral pran yon ti kras plis espas ak tan pase JSON tèks klè, fè operasyon sou li se pi vit. JSONB sipòte tou Indexing, ki fè demann menm pi vit.

Kalite done JSONB a pèmèt nou ranplase modèl EAV ankonbran an lè nou ajoute yon sèl kolòn JSONB sou tab antite nou an, sa ki senplifye konsepsyon baz done a anpil. Men, anpil diskite ke sa a ta dwe akonpaye pa yon diminisyon nan pwodiktivite ... Se poutèt sa mwen te ekri atik sa a.

Mete kanpe yon baz done tès

Pou konparezon sa a, mwen te kreye baz done a sou yon nouvo enstalasyon PostgreSQL 9.5 sou $80 bati a. DigitalOcean Ubuntu 14.04. Apre mete kèk paramèt nan postgresql.conf mwen kouri sa a script lè l sèvi avèk psql. Tablo sa yo te kreye pou prezante done yo nan fòm EAV:

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

Anba a se yon tablo kote menm done yo pral estoke, men ak atribi nan yon kolòn kalite JSONB - pwopriyete.

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

Li sanble anpil pi senp, pa vre? Lè sa a, li te ajoute nan tab antite yo (antite & entity_jsonb) 10 milyon dosye, ak kòmsadwa, yo te ranpli tablo a ak menm done yo lè l sèvi avèk modèl EAV la ak apwòch la ak yon kolòn JSONB - entity_jsonb.properties. Kidonk, nou te resevwa plizyè kalite done diferan nan mitan tout seri pwopriyete yo. Egzanp done:

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

Se konsa, kounye a nou gen menm done yo pou tou de opsyon. Ann kòmanse konpare aplikasyon nan travay!

Senplifye konsepsyon ou

Li te deja deklare ke konsepsyon baz done a te senplifye anpil: yon tab, lè l sèvi avèk yon kolòn JSONB pou pwopriyete, olye pou yo itilize twa tab pou EAV. Men, ki jan sa a reflete nan demann? Mete ajou yon pwopriyete antite sanble sa a:

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

Kòm ou ka wè, dènye demann lan pa sanble pi senp. Pou mete ajou valè yon pwopriyete nan yon objè JSONB nou dwe itilize fonksyon an jsonb_set(), epi yo ta dwe pase nouvo valè nou an kòm yon objè JSONB. Sepandan, nou pa bezwen konnen okenn idantifyan davans. Gade nan egzanp EAV a, nou bezwen konnen tou de entity_id ak entity_attribute_id yo nan lòd yo fè aktyalizasyon a. Si ou vle mete ajou yon pwopriyete nan yon kolòn JSONB ki baze sou non objè a, Lè sa a, li nan tout fè nan yon liy senp.

Koulye a, ann chwazi antite nou jis mete ajou dapre nouvo koulè li yo:

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

Mwen panse ke nou ka dakò ke dezyèm lan pi kout (pa gen okenn rantre!), Se poutèt sa, li pi lizib. JSONB genyen isit la! Nou itilize JSON ->> operatè a pou jwenn koulè a ​​kòm yon valè tèks nan objè JSONB la. Genyen tou yon dezyèm fason pou reyalize menm rezilta a nan modèl JSONB lè l sèvi avèk @> operatè a:

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

Sa a se yon ti kras pi konplike: nou tcheke pou wè si objè JSON nan kolòn pwopriyete li yo gen yon objè ki sou bò dwat la nan @> operatè a. Mwens lizib, pi pwodiktif (gade anba a).

An nou fè sèvi ak JSONB menm pi fasil lè ou bezwen chwazi plizyè pwopriyete an menm tan. Sa a se kote apwòch JSONB reyèlman antre nan: nou tou senpleman chwazi pwopriyete kòm kolòn adisyonèl nan seri rezilta nou an san yo pa bezwen pou rantre:

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

Avèk EAV ou pral bezwen 2 rantre pou chak pwopriyete ou vle rechèch. Dapre mwen, demann ki anwo yo montre yon gwo senplifikasyon nan konsepsyon baz done. Gade plis egzanp sou fason pou ekri demann JSONB, tou nan sa a poste.
Koulye a, li lè yo pale sou pèfòmans.

Pwodiktivite

Pou konpare pèfòmans mwen te itilize EXPLIZE ANALIZ nan demann, pou kalkile tan ekzekisyon. Chak demann te egzekite omwen twa fwa paske planifikatè rechèch la pran plis tan premye fwa a. Premye mwen te kouri demann yo san okenn endèks. Li evidan, sa a se te yon avantaj nan JSONB, depi rantre ki nesesè pou EAV pa t 'kapab itilize endèks (jaden kle etranje yo pa te endis). Apre sa, mwen te kreye yon endèks sou 2 kolòn kle etranje yo nan tablo valè EAV, osi byen ke yon endèks. GIN pou yon kolòn JSONB.

Aktyalizasyon done a te montre rezilta sa yo an tèm de tan (an ms). Remake byen ke echèl la se logaritmik:

Ranplase EAV ak JSONB nan PostgreSQL

Nou wè ke JSONB pi vit (> 50000-x) pase EAV si ou pa sèvi ak endèks, pou rezon ki endike anwo a. Lè nou endèks kolòn ak kle prensipal, diferans lan prèske disparèt, men JSONB toujou 1,3 fwa pi vit pase EAV. Remake byen ke endèks la sou kolòn JSONB la pa gen okenn efè isit la paske nou pa itilize kolòn pwopriyete a nan kritè evalyasyon yo.

Pou chwazi done ki baze sou valè pwopriyete a, nou jwenn rezilta sa yo (echèl nòmal):

Ranplase EAV ak JSONB nan PostgreSQL

Ou ka remake ke JSONB ankò travay pi vit pase EAV san endis, men lè EAV ak endis, li toujou ap travay pi vit pase JSONB. Men, Lè sa a, mwen te wè ke fwa yo pou demann JSONB yo te menm, sa a pouse m 'lefèt ke endèks GIN pa travay. Aparamman lè w sèvi ak yon endèks GIN sou yon kolòn ki gen pwopriyete peple, li sèlman pran efè lè w ap itilize operatè enkli @>. Mwen te itilize sa a nan yon nouvo tès epi li te gen yon gwo enpak sou tan an: sèlman 0,153ms! Sa a se 15000 fwa pi vit pase EAV ak 25000 fwa pi vit pase operatè a ->>.

Mwen panse ke li te rapid ase!

Gwosè tab baz done a

Ann konpare gwosè tab yo pou tou de apwòch. Nan psql nou ka montre gwosè a nan tout tab ak endèks lè l sèvi avèk lòd la dti+

Ranplase EAV ak JSONB nan PostgreSQL

Pou apwòch EAV a, gwosè tab yo alantou 3068 MB ak endèks jiska 3427 MB pou yon total de 6,43 GB. Apwòch JSONB itilize 1817 MB pou tab la ak 318 MB pou endèks yo, ki se 2,08 GB. Li sanble 3 fwa mwens! Reyalite sa a sezi m 'yon ti kras paske nou estoke non pwopriyete nan chak objè JSONB.

Men, toujou, nimewo yo pale pou tèt yo: nan EAV nou estoke 2 nonb antye relatif kle etranje pou chak valè atribi, sa ki lakòz 8 byte nan done adisyonèl. Anplis de sa, EAV estoke tout valè pwopriyete yo kòm tèks, pandan y ap JSONB pral sèvi ak valè nimerik ak booleyen anndan kote sa posib, sa ki lakòz yon anprint pi piti.

Rezilta

An jeneral, mwen panse ke ekonomize pwopriyete antite nan fòma JSONB ka fè konsepsyon ak kenbe baz done ou pi fasil. Si w ap kouri yon anpil nan demann, Lè sa a, kenbe tout bagay nan tab la menm jan ak antite a pral aktyèlman travay pi efikas. Ak lefèt ke sa a senplifye entèraksyon ki genyen ant done se deja yon plis, men baz done a ki kapab lakòz se 3 fwa pi piti nan volim.

Epitou, baze sou tès yo fè, nou ka konkli ke pèt pèfòmans yo trè ensiyifyan. Nan kèk ka, JSONB se menm pi vit pase EAV, sa ki fè li menm pi bon. Sepandan, referans sa a nan kou pa kouvri tout aspè (egzanp antite ki gen yon gwo kantite pwopriyete, yon ogmantasyon siyifikatif nan kantite pwopriyete done ki egziste deja,...), kidonk si ou gen nenpòt sijesyon sou fason pou amelyore yo. , tanpri santi yo lib yo kite nan kòmantè yo!

Sous: www.habr.com

Add nouvo kòmantè