TL; DR: JSONB dikare pêşkeftina şema databasê pir hêsan bike bêyî qurbankirina performansa pirsê.
Pîrozbahiyê
Werin em mînakek klasîk a belkî yek ji kevintirîn dozên karanîna di cîhana databasek têkildar (danûstandin) bidin: saziyek me heye, û pêdivî ye ku em hin taybetmendiyên (taybetmendiyên) vê hebûnê biparêzin. Lê dibe ku ne hemî mînak xwedî heman koma taybetmendiyan bin, û dibe ku di pêşerojê de bêtir taybetmendî werin zêdekirin.
Rêya herî hêsan a çareserkirina vê pirsgirêkê ev e ku meriv di tabloya databasê de ji bo her nirxa milkê stûnek biafirîne, û bi tenê yên ku ji bo mînakek saziyek taybetî hewce ne tije bike. Ecêb! Pirsgirêk çareser bû... heya ku tabloya we bi mîlyonan tomar negire û hûn hewce ne ku tomarek nû lê zêde bikin.
Nimûneya EAV-ê bifikirin (), pir caran pêk tê. Tabloyek saziyan (qeyd) vedihewîne, tabloyek din navên taybetmendiyan (taybetmendan) vedihewîne, û tabloyek sêyemîn saziyan bi taybetmendiyên wan ve girêdide û nirxa wan taybetmendiyan ji bo hebûna heyî vedihewîne. Ev ji we re şansê dide ku hûn ji bo tiştên cihêreng xwedan taybetmendiyên cihêreng bin, û di heman demê de bêyî guheztina avahiya databasê taybetmendiyan zêde bikin.
Lêbelê, ez ê vê postê nenivîsim heke hin kêmasiyên nêzîkatiya EVA nebin. Ji ber vê yekê, mînakî, ji bo bidestxistina yek an çend sazûmanên ku her yek xwedan 1 taybetmendî ne, di pirsê de 2 tevlêbûn hewce ne: ya yekem bi tabloya taybetmendiyê re, ya duyemîn bi tabloya nirxan re têkildar e. Ger hebûnek xwediyê 2 taybetmendiyan be, wê hingê 4 tevlêbûn hewce ne! Wekî din, hemî taybetmendî bi gelemperî wekî rêzan têne hilanîn, ku hem ji bo encam û hem jî ji bo xala WHERE-ê bi tîpan têne hilanîn. Ger hûn gelek pirsan binivîsin, wê hingê ev di warê karanîna çavkaniyê de pir xera ye.
Tevî van kêmasiyên eşkere, EAV demek dirêj e ku ji bo çareserkirina van celeb pirsgirêkan tê bikar anîn. Ev kêmasiyên neçar bûn, û tenê alternatîfek çêtir tune.
Lê paşê "teknolojî" nû di PostgreSQL de xuya bû ...
Bi PostgreSQL 9.4 dest pê kir, celebê daneya JSONB hate zêdekirin da ku daneyên binary JSON hilîne. Her çend hilanîna JSON di vê formatê de bi gelemperî ji nivîsa JSON-ya sade hindiktir cîh û dem digire jî, pêkanîna operasyonên li ser wê pir zûtir e. JSONB di heman demê de îndekskirinê jî piştgirî dike, ku pirsan hê zûtir dike.
Tîpa daneya JSONB dihêle ku em li şûna şêwaza EAV-ya giran biguhezînin bi tenê stûnek JSONB li tabloya saziya xwe zêde bikin, sêwirana databasê pir hêsan dike. Lê gelek kes dibêjin ku divê ev yek bi kêmbûna hilberînê re jî hebe... Ji ber vê yekê min ev gotar nivîsî.
Sazkirina databasek testê
Ji bo vê berhevdanê, min databasa li ser sazkirinek nû ya PostgreSQL 9.5 li ser avakirina 80 $ çêkir. Ubuntu 14.04 Piştî mîhengkirina hin parametreyan di postgresql.conf de, min skrîpta ku psql bikar tîne. Ji bo pêşkêşkirina daneyan di forma EAV de tabloyên jêrîn hatine afirandin:
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
);
Li jêr tabloyek heye ku dê heman daneyan were hilanîn, lê bi taybetmendiyên di stûnek celebek JSONB de - taybetmendiyên.
CREATE TABLE entity_jsonb (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
properties JSONB
);
Pir hêsan xuya dike, ne wusa? Dûv re ew li tabloyên hebûnê hate zêdekirin (dezgehên & entity_jsonb) 10 mîlyon tomar, û li gorî vê yekê, tablo bi karanîna nimûneya EAV û nêzîkbûna bi stûnek JSONB bi heman daneyan hate dagirtin - entity_jsonb.properties. Bi vî rengî, me di nav tevahiya komek taybetmendiyan de çend celeb daneyên cûda wergirtin. Daneyên nimûne:
{
id: 1
name: "Entity1"
description: "Test entity no. 1"
properties: {
color: "red"
lenght: 120
width: 3.1882420
hassomething: true
country: "Belgium"
}
}Ji ber vê yekê naha em ji bo her du vebijarkan heman dane hene. Ka em dest bi berhevkirina pêkanînên li ser kar bikin!
Sêwirana xwe hêsan bikin
Berê hate gotin ku sêwirana databasê pir hêsan bû: yek tablo, bi karanîna stûnek JSONB ji bo taybetmendiyan, li şûna ku sê tabloyên ji bo EAV bikar bînin. Lê ev yek di daxwazan de çawa xuya dike? Nûvekirina yek taybetmendiyê weha xuya dike:
-- 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;
Wekî ku hûn dibînin, daxwaza paşîn hêsantir xuya nake. Ji bo nûvekirina nirxa taybetmendiyek di tiştek JSONB de divê em fonksiyonê bikar bînin , û divê nirxa meya nû wekî tiştek JSONB derbas bike. Lêbelê, ne hewce ye ku em pêşiyê nasnameyek nas bikin. Li mînaka EAV-ê dinêrin, ji bo ku em nûvekirinê pêk bînin divê em hem entity_id û hem jî entity_attribute_id zanibin. Ger hûn dixwazin di stûnek JSONB de li ser bingeha navê objektê taybetmendiyek nûve bikin, wê hingê ew hemî di yek rêzek hêsan de pêk tê.
Naha em li ser bingeha rengê wê yê nû saziya ku me nû nû kiriye hilbijêrin:
-- 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';
Ez difikirim ku em dikarin bipejirînin ku ya duyemîn kurttir e (bê tevlêbûn!), û ji ber vê yekê bêtir xwendin. JSONB li vir serdikeve! Em operatorê JSON ->> bikar tînin da ku reng wekî nirxek nivîsê ji tiştê JSONB bistînin. Di heman demê de rêyek duyemîn jî heye ku meriv di modelek JSONB de heman encamê bi karanîna operatorê @> bi dest bixe:
-- JSONB
SELECT name
FROM entity_jsonb
WHERE properties @> '{"color": "blue"}';
Ev hinekî tevlihevtir e: em kontrol dikin ku bibînin ka tiştê JSON di stûna taybetmendiyên xwe de tiştek heye ku li rastê operatorê @> ye. Kêmtir tê xwendin, bêtir hilber (li jêr binêre).
Werin em karanîna JSONB-ê hê hêsantir bikin dema ku hûn hewce ne ku yekcar gelek taybetmendiyan hilbijêrin. Ev e cihê ku nêzîkatiya JSONB bi rastî tê: em bêyî hewcedariya tevlêbûnê di koma encamên xwe de tenê wekî stûnên pêvek hildibijêrin:
-- JSONB
SELECT name
, properties ->> 'color'
, properties ->> 'country'
FROM entity_jsonb
WHERE id = 120;
Bi EAV re ji bo her milkê ku hûn dixwazin bipirsin hûn ê hewceyê 2 tevlêbûnê bin. Bi dîtina min, pirsên jorîn di sêwirana databasê de sadebûnek mezin nîşan didin. Zêdetir mînakan bibînin ka meriv çawa pirsên JSONB dinivîse, di heman demê de koz.
Niha dema axaftinê li ser performansê ye.
Berhemdariyê
Ji bo berhevdana performansê min bikar anî di pirsan de, ji bo hesabkirina dema darvekirinê. Her pirsek herî kêm sê caran hate darve kirin ji ber ku plansazkerê pirsê cara yekem dirêjtir digire. Pêşî min lêpirsînan bêyî ti nîşanekan meşandin. Eşkere ye, ev avantajek JSONB bû, ji ber ku tevlêbûnên ku ji bo EAV-ê hewce ne, nekarin navnîşan bikar bînin (qavên sereke yên biyanî nehatin îndeks kirin). Piştî vê yekê min li ser 2 stûnên mifteya biyanî yên tabloya nirxê EAV, û her weha pêdekekek çêkir. ji bo stûnek JSONB.
Nûvekirina daneyê encamên jêrîn di warê demê de (di ms) de nîşan da. Bala xwe bidinê ku pîvan logarîtmîkî ye:

Em dibînin ku JSONB ji EAV-ê pir (> 50000-x) zûtir e heke hûn indexan bikar neynin, ji ber sedema ku li jor hatî destnîşan kirin. Gava ku em stûnên bi bişkojkên seretayî nîşan didin, cûdahî hema hema winda dibe, lê JSONB hîn jî 1,3 carî ji EAV zûtir e. Bala xwe bidinê ku pêveka li ser stûna JSONB li vir bandorek nake ji ber ku em di pîvanên nirxandinê de stûna milkê bikar naynin.
Ji bo hilbijartina daneyan li ser bingeha nirxa xanî, em encamên jêrîn digirin (pîvana normal):

Hûn dikarin bala xwe bidin ku JSONB dîsa ji EAV bêyî indexan zûtir dixebite, lê gava ku EAV bi indexan re, ew dîsa jî ji JSONB zûtir dixebite. Lê dûv re min dît ku demên ji bo pirsên JSONB yek in, vê yekê ji min re hişt ku pêvekên GIN nexebitin. Xuya ye dema ku hûn li ser stûnek bi taybetmendiyên niştecîh ve pêdekek GIN bikar tînin, ew tenê dema ku operatorê tevlê @> bikar tîne bandor dike. Min ev di ceribandinek nû de bikar anî û wê bandorek mezin li ser demê kir: tenê 0,153ms! Ev 15000 carî ji EAV û 25000 carî ji operator ->> zûtir e.
Ez difikirim ku ew têra xwe zû bû!
Mezinahiya sifrê Database
Ka em ji bo her du nêzîkatiyan mezinahiyên tabloyê bidin ber hev. Di psql de em dikarin bi karanîna fermanê mezinahiya hemî tablo û navnîşan nîşan bidin dti+

Ji bo nêzîkatiya EAV, mezinahiyên tabloyê li dora 3068 MB in û bi tevahî 3427 GB heya 6,43 MB nîşan dide. Nêzîkatiya JSONB 1817 MB ji bo tabloyê û 318 MB ji bo pêvekan, ku 2,08 GB ye, bikar tîne. Ew 3 carî kêmtir dibe! Vê rastiyê ez hinekî şaş kirim ji ber ku em navên milkan di her tiştê JSONB de hilînin.
Lê dîsa jî, jimar bi xwe diaxivin: Di EAV de em 2 bişkojkên biyanî yên bêkêmasî li her nirxa taybetmendiyê hilînin, ku di encamê de 8 bayt daneya zêde çêdibe. Wekî din, EAV hemî nirxên milkê wekî nivîsê hilîne, dema ku JSONB dê li hundurê ku gengaz be nirxên hejmarî û boolean bikar bîne, û di encamê de şopek piçûktir çêdibe.
Encam
Bi tevayî, ez difikirim ku hilanîna taybetmendiyên saziyê di formata JSONB de dikare sêwirandin û domandina databasa we pir hêsantir bike. Ger hûn gelek pirsan dimeşînin, wê hingê girtina her tiştî di heman tabloyê de wekî saziyê dê bi rastî bi bandortir bixebite. Û rastiya ku ev danûstendina di navbera daneyan de hêsan dike jixwe zêdeyek e, lê databasa encam 3 carî bi qebareyê piçûktir e.
Di heman demê de, li ser bingeha ceribandinên hatine kirin, em dikarin encam bidin ku windahiyên performansê pir ne girîng in. Di hin rewşan de, JSONB ji EAV jî zûtir e, ew hîn çêtir dike. Lêbelê, ev pîvan helbet hemî aliyan venagire (mînak sazûmanên bi hejmareke pir mezin a taybetmendiyan, zêdebûnek berbiçav di hejmara taybetmendiyên daneyên heyî de, ...), ji ber vê yekê heke pêşniyarên we hebin ka meriv çawa wan çêtir dike. , ji kerema xwe di şîroveyan de bihêlin!
Source: www.habr.com
