TL; DR: JSONB метавонад бидуни кам кардани иҷрои дархостҳо таҳияи схемаи пойгоҳи додаҳоро хеле содда кунад.
Муқаддима
Биёед як мисоли классикиро гирем, эҳтимол яке аз қадимтарин ҳолатҳои истифода дар ҷаҳони пойгоҳи додаҳои релятсионӣ: мо як объект дорем ва мо бояд хосиятҳои (атрибутҳои) муайяни ин объектро нигоҳ дорем. Аммо на ҳама мисолҳо метавонанд маҷмӯи хосиятҳои якхела дошта бошанд ва дар оянда хосиятҳои бештар илова карда шаванд.
Соддатарин роҳи ҳалли ин мушкилот эҷод кардани сутун дар ҷадвали пойгоҳи додаҳо барои ҳар як арзиши амвол ва пур кардани сутунҳое, ки барои мисоли мушаххас заруранд. Аҷоиб! Масъала ҳал шуд... то даме ки ҷадвали шумо миллионҳо сабтҳоро дар бар гирад ва шумо бояд сабти нав илова кунед.
Биёед намунаи EAV-ро дида бароем (), хеле маъмул аст. Як ҷадвал дорои объектҳо (сабтҳо), ҷадвали дигар номҳои моликият (атрибутҳо) ва ҷадвали сеюм объектҳоро ба атрибутҳои онҳо пайваст мекунад ва дорои арзишҳои ин атрибутҳо барои объекти ҷорӣ мебошад. Ин ба шумо имкон медиҳад, ки маҷмӯи хосиятҳои объектҳои гуногун дошта бошед, инчунин бидуни тағир додани сохтори пойгоҳи додаҳо хосиятҳои фаврӣ илова кунед.
Бо вуҷуди ин, ман ин мақоларо наменавистам, агар дар равиши EVA камбудиҳо вуҷуд надоштанд. Масалан, дарёфти як ё якчанд объект бо як аттрибутӣ дар дархост ду пайвастшавиро талаб мекунад: пайвасти якум бо ҷадвали атрибутӣ, пайвасти дуюм бо ҷадвали арзиш. Агар объект ду атрибут дошта бошад, пас чор пайвастшавӣ лозим аст! Ғайр аз он, ҳама атрибутҳо одатан ҳамчун сатр нигоҳ дошта мешаванд, ки боиси маҷбуркунии навъи ҳам барои натиҷа ва ҳам банди WHERE мегардад. Агар шумо дархостҳои зиёде нависед, ин аз ҷиҳати истифодаи захираҳо хеле беҳуда аст.
Сарфи назар аз ин камбудиҳои ошкор, EAV кайҳо боз барои ҳалли ин гуна мушкилот истифода мешавад. Инҳо камбудиҳои ногузир буданд ва алтернативаи беҳтаре вуҷуд надошт.
Аммо баъдан дар PostgreSQL "технологияи" нав пайдо шуд…
Аз PostgreSQL 9.4 сар карда, навъи додаҳои JSONB барои нигоҳ доштани маълумоти дуии JSON илова карда шуд. Гарчанде ки нигоҳдории JSON дар ин формат одатан нисбат ба JSON матни оддӣ каме бештар ҷой ва вақтро мегирад, амалиёт бо он хеле тезтар аст. JSONB инчунин индексатсияро дастгирӣ мекунад ва дархостҳоро боз ҳам тезтар мекунад.
Навъи маълумоти JSONB ба мо имкон медиҳад, ки намунаи вазнини EAV-ро бо илова кардани сутуни ягонаи JSONB ба ҷадвали объекти худ иваз кунем ва тарҳи пойгоҳи додаҳоро ба таври назаррас содда созем. Бо вуҷуди ин, бисёриҳо баҳс мекунанд, ки ин барои иҷроиш арзиш дорад… Барои ҳамин ман ин мақоларо навиштам.
Ташкили базаи санҷишӣ
Барои ин муқоиса, ман пойгоҳи додаҳоро дар насби нави PostgreSQL 9.5 дар $80 сохтам. Ubuntu 14.04 Пас аз танзим кардани баъзе параметрҳо дар postgresql.conf ман иҷро кардам скрипт бо истифода аз psql. Барои пешниҳоди маълумот ҳамчун 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
);
Дар зер ҷадвале мавҷуд аст, ки дар он ҳамон маълумот нигоҳ дошта мешавад, аммо бо атрибутҳо дар сутуни JSONB - объектҳо.
CREATE TABLE entity_jsonb (
id SERIAL PRIMARY KEY,
name TEXT,
description TEXT,
properties JSONB
);
Ба назар хеле соддатар аст, ҳамин тавр не? Сипас он ба ҷадвалҳои объект илова карда шуд (шахси & entity_jsonb) 10 миллион сабт, ва мувофиқан, ҷадвал бо маълумоти якхела пур карда шуд, ки дар он намунаи EAV ва равиш бо сутуни JSONB истифода шудааст - entity_jsonb.propertiesҲамин тариқ, мо дар тамоми маҷмӯи хосиятҳо якчанд намудҳои гуногуни маълумотро ба даст овардем. Намунаи маълумот:
{
id: 1
name: "Entity1"
description: "Test entity no. 1"
properties: {
color: "red"
lenght: 120
width: 3.1882420
hassomething: true
country: "Belgium"
}
}Ҳамин тавр, ҳоло мо барои ҳарду вариант маълумоти якхела дорем. Биёед ба муқоисаи татбиқҳо дар ҳаёти воқеӣ шурӯъ кунем!
Соддасозии тарҳрезӣ
Пештар зикр шуда буд, ки тарҳи пойгоҳи додаҳо ба таври назаррас содда карда шуд: як ҷадвал, бо истифода аз сутуни JSONB барои хосиятҳо, ба ҷои се ҷадвал барои EAV. Аммо ин чӣ гуна ба пурсишҳо табдил меёбад? Навсозии моликияти ягона чунин менамояд:
-- 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;
Тавре ки шумо мебинед, дархости охирин соддатар ба назар намерасад. Барои навсозии арзиши амвол дар объекти JSONB, мо бояд функсияро истифода барем , ва бояд арзиши нави моро ҳамчун объекти JSONB гузаронад. Аммо, ба мо лозим нест, ки ягон идентификаторро пешакӣ донем. Ба мисоли EAV нигоҳ карда, мо бояд ҳам entity_id ва ҳам entity_attribute_id-ро донем, то навсозӣ навсозӣ шавад. Агар шумо хоҳед, ки амволро дар сутуни JSONB дар асоси номи объект навсозӣ кунед, он ҳама дар як сатри оддӣ анҷом дода мешавад.
Акнун биёед объектеро, ки мо навсозӣ кардем, дар асоси ранги нави он интихоб кунем:
-- 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';
Ман фикр мекунам, ки мо метавонем розӣ шавем, ки дуюм кӯтоҳтар аст (бе ҳамроҳшавӣ!) ва бинобар ин хондан бештар. JSONB дар ин ҷо пирӯз мешавад! Мо оператори JSON ->> -ро барои гирифтани ранг ҳамчун арзиши матн аз объекти JSONB истифода мебарем. Инчунин роҳи дуюми ба даст овардани ҳамон натиҷа дар модели JSONB бо истифода аз оператори @> мавҷуд аст:
-- JSONB
SELECT name
FROM entity_jsonb
WHERE properties @> '{"color": "blue"}';
Ин каме мураккабтар аст: мо тафтиш мекунем, ки оё объекти JSON дар сутуни хосиятҳо объектро дар тарафи рости оператори @> дар бар мегирад. Камтар хондан, иҷрокунандатар (нигаред ба поён).
Биёед истифодаи JSONB-ро боз ҳам соддатар кунем, вақте ки шумо бояд дар як вақт якчанд хосиятҳоро интихоб кунед. Ин аст, ки равиши JSONB воқеан дурахшон аст: мо танҳо хосиятҳоро ҳамчун сутунҳои иловагӣ дар маҷмӯи натиҷаҳои худ бидуни зарурати пайваст интихоб мекунем:
-- JSONB
SELECT name
, properties ->> 'color'
, properties ->> 'country'
FROM entity_jsonb
WHERE id = 120;
Бо EAV, ба шумо барои ҳар як амволе, ки мехоҳед дархост кунед, ду пайвастшавӣ лозим аст. Ба андешаи ман, дархостҳои дар боло овардашуда соддасозии назаррасро дар тарҳрезии пойгоҳи додаҳо нишон медиҳанд. Шумо инчунин метавонед мисолҳои бештареро дар бораи чӣ гуна навиштани дархостҳои JSONB дар пост.
Акнун вақти он расидааст, ки дар бораи иҷроиш сӯҳбат кунем.
Маҳсулнокӣ
Барои муқоисаи иҷрои ман истифода дар дархостҳо барои ҳисоб кардани вақти иҷро. Ҳар як дархост на камтар аз се маротиба иҷро шуд, зеро банақшагирии дархост бори аввал тӯл мекашад. Аввалан, ман дархостҳоро бе ягон индекс иҷро кардам. Ин бешубҳа бартарии JSONB буд, зеро пайвастагиҳои барои EAV зарурӣ индексҳоро истифода бурда наметавонанд (майдонҳои калидии хориҷӣ индексатсия карда нашудаанд). Пас аз он, ман дар ду сутуни калидии хориҷии ҷадвали арзиши EAV индекс ва инчунин индекс эҷод кардам барои сутуни JSONB.
Навсозии маълумот натиҷаҳои зерини вақтро нишон дод (бо ms). Дар хотир доред, ки миқёс логарифмикӣ аст:

Мо мебинем, ки JSONB ба таври назаррас (> 50000x) нисбат ба EAV бидуни индексатсия бо сабаби дар боло зикршуда тезтар аст. Вақте ки мо сутунҳои калиди ибтидоиро индексатсия мекунем, фарқият қариб нест мешавад, аммо JSONB то ҳол нисбат ба EAV 1,3 маротиба тезтар аст. Дар хотир доред, ки шохиси сутуни JSONB дар ин ҷо ҳеҷ таъсире надорад, зеро мо сутуни амволро дар меъёрҳои арзёбӣ истифода намебарем.
Барои интихоби маълумот дар асоси арзиши амвол, мо натиҷаҳои зеринро мегирем (миқёси муқаррарӣ):

Шумо мебинед, ки JSONB боз аз EAV бе индекс тезтар аст, аммо вақте ки EAV индексатсия карда мешавад, он ҳанӯз ҳам аз JSONB тезтар аст. Аммо баъд ман дидам, ки вақтҳои дархостҳои JSONB якхелаанд, ки ин маро ба он овард, ки индексҳои GIN оғоз намешаванд. Эҳтимол, вақте ки шумо индекси GIN-ро дар сутуни дорои хосиятҳои пуршуда истифода мекунед, он танҳо ҳангоми истифодаи оператори дохилкунии @> эътибор пайдо мекунад. Ман инро дар озмоиши нав истифода кардам ва он ба вақт таъсири бузург расонд: ҳамагӣ 0,153 мс! Ин назар ба EAV 15 000 маротиба ва аз оператори ->> 25 000 маротиба тезтар аст.
Ман фикр мекунам, ки ин хеле зуд буд!
Андозаи ҷадвалҳои пойгоҳи додаҳо
Биёед андозаи ҷадвалро барои ҳарду равиш муқоиса кунем. Дар psql мо метавонем бо ёрии фармон андозаи тамоми ҷадвалҳо ва индексҳоро нишон диҳем dti+

Бо равиши EAV, андозаи ҷадвал тақрибан 3068 МБ ва индексҳо то 3427 МБ, ҳамагӣ 6,43 ГБ мебошанд. Бо истифода аз равиши JSONB, ҷадвал 1817 МБ ва индекси 318 МБ, дар маҷмӯъ 2,08 ГБ-ро истифода мебарад. Ин сеяки андоза аст! Ин далел маро каме ба ҳайрат овард, зеро мо номҳои амволро дар ҳар як объекти JSONB нигоҳ медорем.
Аммо рақамҳо барои худ сухан мегӯянд: дар EAV, мо ду калиди хориҷиро барои як арзиши атрибут нигоҳ медорем, ки дар натиҷа 8 байт маълумоти иловагӣ ба даст меояд. Ғайр аз он, дар EAV, ҳама арзишҳои амвол ҳамчун матн нигоҳ дошта мешаванд, дар ҳоле ки JSONB дар ҳолати имконпазир арзишҳои ададӣ ва мантиқиро дар дохили он истифода мебарад, ки дар натиҷа изи хурдтар мешавад.
Натиҷаҳо
Дар маҷмӯъ, ман фикр мекунам, ки нигоҳ доштани хосиятҳои объект дар формати JSONB метавонад тарҳрезӣ ва нигоҳдории пойгоҳи додаҳои шуморо ба таври назаррас содда кунад. Агар шумо дархостҳои зиёдеро иҷро кунед, нигоҳ доштани ҳама чиз дар як ҷадвал бо объект воқеан самараноктар хоҳад буд. Далели он, ки он муоширати маълумотро содда мекунад, аллакай як плюс аст, аммо пойгоҳи додаи натиҷавӣ низ аз андозаи се маротиба хурдтар аст.
Инчунин, дар асоси натиҷаҳои муқоисавӣ, мо метавонем хулоса барорем, ки ҷазои иҷроиш хеле ночиз аст. Дар баъзе ҳолатҳо, JSONB ҳатто нисбат ба EAV тезтар кор мекунад ва онро боз ҳам беҳтар мекунад. Бо вуҷуди ин, ин нишондиҳанда албатта ҳама ҷанбаҳоро дар бар намегирад (масалан, субъектҳои дорои шумораи хеле зиёди моликият, афзоиши назарраси шумораи моликият дар маълумоти мавҷуда ва ғайра), бинобар ин, агар шумо ягон пешниҳоде оид ба беҳтарсозӣ дошта бошед, лутфан озодона онҳоро дар шарҳҳо гузоред!
Манбаъ: will.com
