Kubadilisha EAV na JSONB katika PostgreSQL

TL; DR: JSONB inaweza kurahisisha sana uundaji wa schema ya hifadhidata bila kughairi utendakazi wa hoja.

Utangulizi

Hebu tutoe mfano wa kawaida wa pengine mojawapo ya kesi kongwe zaidi za utumiaji katika ulimwengu wa hifadhidata ya uhusiano (database): tuna huluki, na tunahitaji kuhifadhi baadhi ya mali (sifa) za huluki hii. Lakini sio matukio yote yanaweza kuwa na seti sawa ya mali, na mali zaidi inaweza kuongezwa katika siku zijazo.

Njia rahisi zaidi ya kutatua tatizo hili ni kuunda safu katika jedwali la hifadhidata kwa kila thamani ya mali, na kujaza tu zile zinazohitajika kwa mfano maalum wa chombo. Kubwa! Tatizo limetatuliwa... hadi jedwali lako liwe na mamilioni ya rekodi na unahitaji kuongeza rekodi mpya.

Fikiria muundo wa EAV (Thamani-Sifa-Huluki), hutokea mara nyingi kabisa. Jedwali moja lina huluki (rekodi), jedwali lingine lina majina ya sifa (sifa), na jedwali la tatu hushirikisha huluki na sifa zao na lina thamani ya sifa hizo kwa huluki ya sasa. Hii inakupa uwezo wa kuwa na seti tofauti za mali kwa vitu tofauti, na pia kuongeza mali kwenye nzi bila kubadilisha muundo wa hifadhidata.

Walakini, singekuwa nikiandika chapisho hili ikiwa hakungekuwa na mapungufu kwa mbinu ya EVA. Kwa hivyo, kwa mfano, kupata chombo kimoja au zaidi ambacho kina sifa 1 kila moja, viunga 2 vinahitajika katika swali: ya kwanza ni kuunganishwa na jedwali la sifa, la pili ni unganisho na jedwali la maadili. Ikiwa huluki ina sifa 2, basi viungio 4 vinahitajika! Zaidi ya hayo, sifa zote kwa kawaida huhifadhiwa kama mifuatano, ambayo husababisha utumaji wa aina kwa matokeo na kifungu cha WHERE. Ikiwa unaandika maswali mengi, basi hii ni kupoteza kabisa katika suala la matumizi ya rasilimali.

Licha ya mapungufu haya dhahiri, EAV imetumika kwa muda mrefu kutatua aina hizi za shida. Haya yalikuwa mapungufu yasiyoepukika, na hakukuwa na njia bora zaidi.
Lakini basi "teknolojia" mpya ilionekana katika PostgreSQL ...

Kuanzia na PostgreSQL 9.4, aina ya data ya JSONB iliongezwa ili kuhifadhi data ya jozi ya JSON. Ingawa kuhifadhi JSON katika umbizo hili kwa kawaida huchukua nafasi na wakati zaidi kuliko maandishi wazi ya JSON, kuifanyia kazi ni haraka zaidi. JSONB pia inasaidia kuorodhesha, ambayo hufanya maswali kuwa haraka zaidi.

Aina ya data ya JSONB huturuhusu kuchukua nafasi ya muundo mzito wa EAV kwa kuongeza safu wima moja tu ya JSONB kwenye jedwali letu la huluki, na hivyo kurahisisha sana muundo wa hifadhidata. Lakini wengi wanasema kwamba hii inapaswa kuambatana na kupungua kwa tija ... Ndiyo maana niliandika makala hii.

Kuweka hifadhidata ya majaribio

Kwa kulinganisha huku, niliunda hifadhidata kwenye usakinishaji mpya wa PostgreSQL 9.5 kwenye ujenzi wa $80. DigitalOcean Ubuntu 14.04. Baada ya kuweka baadhi ya vigezo katika postgresql.conf nilikimbia hii hati kwa kutumia psql. Jedwali zifuatazo ziliundwa ili kuwasilisha data katika fomu ya 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
);

Hapo chini kuna jedwali ambalo data sawa itahifadhiwa, lakini ikiwa na sifa kwenye safu ya aina ya JSONB - mali.

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

Inaonekana rahisi zaidi, sivyo? Kisha iliongezwa kwenye jedwali la chombo (chombo & chombo_jsonb) Rekodi milioni 10, na ipasavyo, jedwali lilijazwa na data sawa kwa kutumia muundo wa EAV na mbinu iliyo na safu wima ya JSONB - entity_jsonb.properties. Kwa hivyo, tulipokea aina kadhaa tofauti za data kati ya seti nzima ya mali. Mfano wa data:

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

Kwa hivyo sasa tuna data sawa kwa chaguo zote mbili. Wacha tuanze kulinganisha utekelezaji kazini!

Rahisisha muundo wako

Hapo awali ilielezwa kuwa muundo wa hifadhidata umerahisishwa sana: jedwali moja, kwa kutumia safu wima ya JSONB kwa mali, badala ya kutumia majedwali matatu kwa EAV. Lakini hii inaonyeshwaje katika maombi? Kusasisha mali ya chombo kimoja inaonekana kama hii:

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

Kama unaweza kuona, ombi la mwisho halionekani rahisi. Ili kusasisha thamani ya mali katika kitu cha JSONB inabidi kutumia chaguo hili la kukokotoa jsonb_set(), na inapaswa kupitisha thamani yetu mpya kama kitu cha JSONB. Hata hivyo, hatuhitaji kujua kitambulisho chochote mapema. Kwa kuangalia mfano wa EAV, tunahitaji kujua entity_id na entity_attribute_id ili kutekeleza sasisho. Ikiwa unataka kusasisha mali katika safu wima ya JSONB kulingana na jina la kitu, basi yote yanafanywa kwa mstari mmoja rahisi.

Sasa hebu tuchague huluki ambayo tumesasisha hivi punde kulingana na rangi yake mpya:

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

Nadhani tunaweza kukubaliana kwamba ya pili ni fupi (hakuna kujiunga!), na kwa hiyo inasomeka zaidi. JSONB itashinda hapa! Tunatumia opereta ya JSON ->> kupata rangi kama thamani ya maandishi kutoka kwa kitu cha JSONB. Pia kuna njia ya pili ya kupata matokeo sawa katika mfano wa JSONB kwa kutumia @> operator:

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

Hii ni ngumu zaidi: tunaangalia ili kuona ikiwa kitu cha JSON kwenye safu wima yake ya mali kina kitu ambacho kiko upande wa kulia wa opereta @>. Haisomeki, ina tija zaidi (tazama hapa chini).

Wacha tufanye kutumia JSONB iwe rahisi zaidi unapohitaji kuchagua mali nyingi mara moja. Hapa ndipo mbinu ya JSONB inapoingia: tunachagua tu mali kama safu wima za ziada katika seti yetu ya matokeo bila hitaji la viungio:

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

Ukiwa na EAV utahitaji viungio 2 kwa kila mali unayotaka kuuliza. Kwa maoni yangu, maswali hapo juu yanaonyesha kurahisisha sana katika muundo wa hifadhidata. Tazama mifano zaidi ya jinsi ya kuandika maswali ya JSONB, pia katika hii chapisho.
Sasa ni wakati wa kuzungumza juu ya utendaji.

Uzalishaji

Ili kulinganisha utendaji nilitumia ELEZA CHAMBUA katika maswali, kukokotoa muda wa utekelezaji. Kila hoja ilitekelezwa angalau mara tatu kwa sababu kipangaji hoja huchukua muda mrefu zaidi mara ya kwanza. Kwanza niliendesha maswali bila faharisi yoyote. Ni wazi, hii ilikuwa faida ya JSONB, kwa kuwa viungio vinavyohitajika kwa EAV havikuweza kutumia faharasa (sehemu muhimu za kigeni hazikuorodheshwa). Baada ya haya niliunda faharisi kwenye safu wima 2 za funguo za kigeni za jedwali la thamani la EAV, na faharisi GIN kwa safu ya JSONB.

Sasisho la data lilionyesha matokeo yafuatayo kulingana na wakati (katika ms). Kumbuka kuwa kiwango ni logarithmic:

Kubadilisha EAV na JSONB katika PostgreSQL

Tunaona kuwa JSONB ina kasi zaidi (> 50000-x) kuliko EAV ikiwa hutumii faharasa, kwa sababu iliyotajwa hapo juu. Tunapoorodhesha safu wima zilizo na funguo msingi, tofauti hiyo inakaribia kutoweka, lakini JSONB bado ina kasi mara 1,3 kuliko EAV. Kumbuka kuwa faharasa kwenye safu wima ya JSONB haina athari hapa kwa kuwa hatutumii safu wima ya sifa katika vigezo vya tathmini.

Kwa kuchagua data kulingana na thamani ya mali, tunapata matokeo yafuatayo (kiwango cha kawaida):

Kubadilisha EAV na JSONB katika PostgreSQL

Unaweza kugundua kuwa JSONB inafanya kazi tena haraka kuliko EAV bila faharisi, lakini EAV ikiwa na faharasa, bado inafanya kazi haraka kuliko JSONB. Lakini basi nikaona kuwa nyakati za maswali ya JSONB zilikuwa sawa, hii ilinisukuma kwa ukweli kwamba faharisi za GIN hazifanyi kazi. Inavyoonekana unapotumia faharisi ya GIN kwenye safu wima iliyo na mali nyingi, inachukua athari tu wakati wa kutumia pamoja na opereta @>. Nilitumia hii kwenye jaribio jipya na ilikuwa na athari kubwa kwa wakati huo: 0,153ms tu! Hii ni mara 15000 zaidi ya EAV na mara 25000 kuliko opereta ->>.

Nadhani ilikuwa haraka vya kutosha!

Ukubwa wa jedwali la hifadhidata

Wacha tulinganishe saizi za meza kwa njia zote mbili. Katika psql tunaweza kuonyesha saizi ya meza na faharisi zote kwa kutumia amri dti+

Kubadilisha EAV na JSONB katika PostgreSQL

Kwa mbinu ya EAV, ukubwa wa jedwali ni karibu 3068 MB na faharasa hadi 3427 MB kwa jumla ya GB 6,43. Mbinu ya JSONB hutumia MB 1817 kwa jedwali na MB 318 kwa faharasa, ambayo ni GB 2,08. Inageuka mara 3 chini! Ukweli huu ulinishangaza kidogo kwa sababu tunahifadhi majina ya mali katika kila kitu cha JSONB.

Lakini bado, nambari zinajieleza zenyewe: katika EAV tunahifadhi funguo 2 kamili za kigeni kwa kila thamani ya sifa, na kusababisha baiti 8 za data ya ziada. Zaidi ya hayo, EAV huhifadhi thamani zote za mali kama maandishi, huku JSONB itatumia thamani za nambari na boolean ndani inapowezekana, na kusababisha alama ndogo zaidi.

Matokeo ya

Kwa jumla, nadhani kuhifadhi sifa za huluki katika umbizo la JSONB kunaweza kurahisisha kubuni na kudumisha hifadhidata yako. Ikiwa unauliza maswali mengi, basi kuweka kila kitu kwenye jedwali sawa na chombo kitafanya kazi kwa ufanisi zaidi. Na ukweli kwamba hii hurahisisha mwingiliano kati ya data tayari ni pamoja, lakini hifadhidata inayosababishwa ni ndogo mara 3 kwa kiasi.

Pia, kulingana na vipimo vilivyofanywa, tunaweza kuhitimisha kuwa hasara za utendaji ni ndogo sana. Katika baadhi ya matukio, JSONB ina kasi zaidi kuliko EAV, na kuifanya kuwa bora zaidi. Walakini, alama hii bila shaka haijumuishi vipengele vyote (k.m. huluki zilizo na idadi kubwa ya mali, ongezeko kubwa la idadi ya mali ya data iliyopo,...), kwa hivyo ikiwa una maoni yoyote ya jinsi ya kuziboresha. , tafadhali jisikie huru kuondoka kwenye maoni!

Chanzo: mapenzi.com

Kuongeza maoni