टीएल; डॉ: JSONB क्वेरी प्रदर्शन से समझौता किए बिना डेटाबेस स्कीमा विकास को बहुत सरल बना सकता है।
परिचय
आइए रिलेशनल डेटाबेस (डेटाबेस) की दुनिया में संभवतः सबसे पुराने उपयोग मामलों में से एक का एक उत्कृष्ट उदाहरण दें: हमारे पास एक इकाई है, और हमें इस इकाई के कुछ गुणों (विशेषताओं) को सहेजने की आवश्यकता है। लेकिन सभी उदाहरणों में गुणों का एक ही सेट नहीं हो सकता है, और भविष्य में और अधिक गुण जोड़े जा सकते हैं।
इस समस्या को हल करने का सबसे आसान तरीका प्रत्येक संपत्ति मूल्य के लिए डेटाबेस तालिका में एक कॉलम बनाना है, और बस उन लोगों को भरना है जो एक विशिष्ट इकाई उदाहरण के लिए आवश्यक हैं। महान! समस्या हल हो गई... जब तक कि आपकी तालिका में लाखों रिकॉर्ड न हों और आपको एक नया रिकॉर्ड जोड़ने की आवश्यकता न हो।
ईएवी पैटर्न पर विचार करें (
हालाँकि, यदि ईवीए दृष्टिकोण में कुछ कमियाँ न होतीं तो मैं यह पोस्ट नहीं लिख रहा होता। इसलिए, उदाहरण के लिए, एक या अधिक संस्थाओं को प्राप्त करने के लिए जिनमें प्रत्येक में 1 विशेषता है, क्वेरी में 2 जुड़ाव की आवश्यकता होती है: पहला विशेषता तालिका के साथ जुड़ाव है, दूसरा मान तालिका के साथ जुड़ाव है। यदि किसी इकाई में 2 विशेषताएँ हैं, तो 4 जोड़ की आवश्यकता है! इसके अतिरिक्त, सभी विशेषताओं को आमतौर पर स्ट्रिंग्स के रूप में संग्रहीत किया जाता है, जिसके परिणामस्वरूप परिणाम और WHERE क्लॉज दोनों के लिए टाइप कास्टिंग होती है। यदि आप बहुत सारी क्वेरीज़ लिखते हैं, तो संसाधन उपयोग की दृष्टि से यह काफी बेकार है।
इन स्पष्ट कमियों के बावजूद, इस प्रकार की समस्याओं को हल करने के लिए ईएवी का उपयोग लंबे समय से किया जाता रहा है। ये अपरिहार्य कमियाँ थीं, और इससे बेहतर कोई विकल्प नहीं था।
लेकिन फिर PostgreSQL में एक नई "तकनीक" सामने आई...
PostgreSQL 9.4 से शुरू करके, JSON बाइनरी डेटा को संग्रहीत करने के लिए JSONB डेटा प्रकार जोड़ा गया था। हालाँकि इस प्रारूप में JSON को संग्रहीत करने में आम तौर पर सादे पाठ JSON की तुलना में थोड़ा अधिक स्थान और समय लगता है, लेकिन इस पर संचालन करना बहुत तेज़ है। JSONB इंडेक्सिंग का भी समर्थन करता है, जिससे क्वेरीज़ और भी तेज़ हो जाती हैं।
JSONB डेटा प्रकार हमें हमारी इकाई तालिका में केवल एक JSONB कॉलम जोड़कर बोझिल EAV पैटर्न को बदलने की अनुमति देता है, जिससे डेटाबेस डिज़ाइन बहुत सरल हो जाता है। लेकिन कई लोगों का तर्क है कि इसके साथ उत्पादकता में कमी होनी चाहिए... इसीलिए मैंने यह लेख लिखा है।
एक परीक्षण डेटाबेस स्थापित करना
इस तुलना के लिए, मैंने $9.5 बिल्ड पर PostgreSQL 80 की ताज़ा स्थापना पर डेटाबेस बनाया
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
);
यह बहुत सरल लगता है, है ना? फिर इसे इकाई तालिकाओं में जोड़ा गया (सत्ता & इकाई_जेएसओएनबी) 10 मिलियन रिकॉर्ड, और तदनुसार, तालिका को ईएवी पैटर्न और जेएसओएनबी कॉलम के साथ दृष्टिकोण का उपयोग करके समान डेटा से भरा गया था - इकाई_jsonb.गुण. इस प्रकार, हमें संपत्तियों के पूरे सेट के बीच कई अलग-अलग डेटा प्रकार प्राप्त हुए। उदाहरण डेटा:
{
id: 1
name: "Entity1"
description: "Test entity no. 1"
properties: {
color: "red"
lenght: 120
width: 3.1882420
hassomething: true
country: "Belgium"
}
}
तो अब हमारे पास दोनों विकल्पों के लिए समान डेटा है। आइए कार्यस्थल पर कार्यान्वयन की तुलना करना शुरू करें!
अपने डिज़ाइन को सरल बनाएं
यह पहले कहा गया था कि डेटाबेस डिज़ाइन को बहुत सरल बनाया गया था: EAV के लिए तीन तालिकाओं का उपयोग करने के बजाय, गुणों के लिए JSONB कॉलम का उपयोग करके एक तालिका। लेकिन यह अनुरोधों में कैसे परिलक्षित होता है? एक इकाई संपत्ति को अद्यतन करना इस तरह दिखता है:
-- 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 ऑब्जेक्ट में किसी प्रॉपर्टी के मूल्य को अपडेट करने के लिए हमें फ़ंक्शन का उपयोग करना होगा
आइए अब उस इकाई का चयन करें जिसे हमने अभी उसके नए रंग के आधार पर अपडेट किया है:
-- 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 यहाँ जीत गया! हम JSONB ऑब्जेक्ट से टेक्स्ट मान के रूप में रंग प्राप्त करने के लिए JSON ->> ऑपरेटर का उपयोग करते हैं। @> ऑपरेटर का उपयोग करके 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;
ईएवी के साथ आपको प्रत्येक संपत्ति के लिए 2 जॉइन की आवश्यकता होगी जिसे आप क्वेरी करना चाहते हैं। मेरी राय में, उपरोक्त प्रश्न डेटाबेस डिज़ाइन में एक महान सरलीकरण दिखाते हैं। JSONB क्वेरीज़ लिखने के तरीके के और भी उदाहरण देखें
अब बारी है परफॉर्मेंस की बात करने की.
निष्पादन
प्रदर्शन की तुलना करने के लिए मैंने इसका उपयोग किया
डेटा अद्यतन ने समय के संदर्भ में (एमएस में) निम्नलिखित परिणाम दिखाए। ध्यान दें कि पैमाना लघुगणकीय है:
हम देखते हैं कि यदि आप ऊपर बताए गए कारण से इंडेक्स का उपयोग नहीं करते हैं, तो JSONB EAV की तुलना में बहुत (> 50000-x) तेज़ है। जब हम प्राथमिक कुंजी के साथ कॉलम को अनुक्रमित करते हैं, तो अंतर लगभग गायब हो जाता है, लेकिन JSONB अभी भी EAV से 1,3 गुना तेज है। ध्यान दें कि JSONB कॉलम पर सूचकांक का यहां कोई प्रभाव नहीं है क्योंकि हम मूल्यांकन मानदंड में संपत्ति कॉलम का उपयोग नहीं कर रहे हैं।
संपत्ति के मूल्य के आधार पर डेटा का चयन करने पर, हमें निम्नलिखित परिणाम (सामान्य पैमाने) मिलते हैं:
आप देख सकते हैं कि JSONB फिर से इंडेक्स के बिना EAV की तुलना में तेजी से काम करता है, लेकिन जब EAV इंडेक्स के साथ होता है, तब भी यह JSONB की तुलना में तेजी से काम करता है। लेकिन फिर मैंने देखा कि JSONB प्रश्नों का समय वही था, इससे मुझे यह तथ्य पता चला कि GIN इंडेक्स काम नहीं करते हैं। जाहिरा तौर पर जब आप पॉपुलेटेड गुणों वाले कॉलम पर GIN इंडेक्स का उपयोग करते हैं, तो यह केवल शामिल ऑपरेटर @> का उपयोग करते समय प्रभावी होता है। मैंने इसे एक नए परीक्षण में उपयोग किया और उस समय इसका बहुत बड़ा प्रभाव पड़ा: केवल 0,153 एमएस! यह ईएवी से 15000 गुना तेज और ->> ऑपरेटर से 25000 गुना तेज है।
मुझे लगता है कि यह काफी तेज़ था!
डेटाबेस तालिका का आकार
आइए दोनों दृष्टिकोणों के लिए तालिका आकारों की तुलना करें। Psql में हम कमांड का उपयोग करके सभी तालिकाओं और इंडेक्स का आकार दिखा सकते हैं डीटीआई+
ईएवी दृष्टिकोण के लिए, तालिका का आकार लगभग 3068 एमबी है और कुल 3427 जीबी के लिए 6,43 एमबी तक अनुक्रमित है। JSONB दृष्टिकोण तालिका के लिए 1817 एमबी और इंडेक्स के लिए 318 एमबी का उपयोग करता है, जो 2,08 जीबी है। यह 3 गुना कम निकला! इस तथ्य ने मुझे थोड़ा आश्चर्यचकित कर दिया क्योंकि हम प्रत्येक JSONB ऑब्जेक्ट में संपत्ति के नाम संग्रहीत करते हैं।
लेकिन फिर भी, संख्याएँ अपने लिए बोलती हैं: ईएवी में हम प्रति विशेषता मान पर 2 पूर्णांक विदेशी कुंजियाँ संग्रहीत करते हैं, जिसके परिणामस्वरूप 8 बाइट्स अतिरिक्त डेटा प्राप्त होता है। इसके अतिरिक्त, ईएवी सभी संपत्ति मूल्यों को पाठ के रूप में संग्रहीत करता है, जबकि जेएसओएनबी जहां संभव हो आंतरिक रूप से संख्यात्मक और बूलियन मूल्यों का उपयोग करेगा, जिसके परिणामस्वरूप एक छोटा पदचिह्न होगा।
परिणाम
कुल मिलाकर, मुझे लगता है कि इकाई संपत्तियों को JSONB प्रारूप में सहेजने से आपके डेटाबेस को डिज़ाइन करना और बनाए रखना बहुत आसान हो सकता है। यदि आप बहुत सारी क्वेरीज़ चला रहे हैं, तो इकाई के रूप में सब कुछ एक ही तालिका में रखने से वास्तव में अधिक कुशलता से काम होगा। और तथ्य यह है कि यह डेटा के बीच इंटरैक्शन को सरल बनाता है, पहले से ही एक प्लस है, लेकिन परिणामी डेटाबेस वॉल्यूम में 3 गुना छोटा है।
इसके अलावा, किए गए परीक्षणों के आधार पर, हम यह निष्कर्ष निकाल सकते हैं कि प्रदर्शन हानि बहुत महत्वहीन है। कुछ मामलों में, JSONB EAV से भी तेज़ है, जो इसे और भी बेहतर बनाता है। हालाँकि, यह बेंचमार्क निश्चित रूप से सभी पहलुओं को कवर नहीं करता है (उदाहरण के लिए बहुत बड़ी संख्या में संपत्तियों वाली इकाइयाँ, मौजूदा डेटा की संपत्तियों की संख्या में उल्लेखनीय वृद्धि,...), इसलिए यदि आपके पास उन्हें सुधारने के बारे में कोई सुझाव है , कृपया बेझिझक टिप्पणी छोड़ें!
स्रोत: www.habr.com