इस लेख में, आप सीखेंगे कि जब आप अपने डेटाबेस को विभाजित करते हैं तो अपने डेटा के पीछे के शब्दार्थ का उपयोग कैसे करें। यह आपके एप्लिकेशन के प्रदर्शन में काफी सुधार कर सकता है। और, सबसे महत्वपूर्ण बात, आप पाएंगे कि आपको अपने विभाजन मानदंड को अपने अद्वितीय एप्लिकेशन डोमेन के अनुरूप बनाना चाहिए।
मैंने खेल विशेषज्ञों के लिए निर्णय लेने और डेटा का पता लगाने के लिए एक वेब ऐप विकसित करने के लिए एक स्टार्टअप के साथ सहयोग किया है। एप्लिकेशन किसी भी खेल का समर्थन करता है, लेकिन हम यूरोप में स्थित हैं - और यूरोपीय फुटबॉल से प्यार करते हैं। दुनिया भर में हर दिन खेले जाने वाले सैकड़ों खेलों में से प्रत्येक हजारों पंक्तियों के साथ आता है। कुछ ही महीनों में, हमारे ऐप में ईवेंट तालिका आधा अरब पंक्तियों तक पहुंच गई!
यह समझकर कि सॉकर विशेषज्ञ हमारे डेटा को कैसे क्वेरी कर रहे थे, हम डेटाबेस को समझदारी से विभाजित कर सकते हैं। इस नई तालिका में औसत समय सुधार 20x और 40x तेज के बीच था। सभी प्रश्नों पर औसत समय सुधार 5X से 10X था।
आइए अब इस परिदृश्य में तल्लीन करें और जानें कि डेटाबेस को विभाजित करते समय आप अपने डेटा संदर्भ को अनदेखा क्यों नहीं कर सकते।
प्रसंग प्रस्तुत करना
हमारा खेल एप्लिकेशन कच्चे और समेकित डेटा दोनों की पेशकश करता है, हालांकि इसे अपनाने वाले पेशेवर बाद वाले को पसंद करते हैं। अंतर्निहित डेटाबेस में कई प्रदाताओं के जटिल, असंरचित, विषम डेटा के टेराबाइट्स होते हैं। इसलिए, सबसे बड़ी चुनौती एक विश्वसनीय, तेज़ और आसानी से खोजे जाने वाला डेटाबेस तैयार करना था।
एप्लिकेशन डोमेन
इस उद्योग में, कई प्रदाता अपने ग्राहकों को सबसे महत्वपूर्ण सॉकर खेलों की घटनाओं तक पहुंच प्रदान करते हैं। विशेष रूप से, वे आपको गेम के दौरान जो हुआ उससे संबंधित डेटा प्रदान करते हैं, जैसे कि गोल, सहायता, पीला कार्ड, पास, और बहुत कुछ। इस डेटा वाली तालिका अब तक की सबसे बड़ी तालिका है जिसके साथ हमें काम करना था।
VPS विनिर्देश, तकनीक और वास्तुकला
मेरी टीम बैकएंड एप्लिकेशन विकसित कर रही है जो सबसे महत्वपूर्ण डेटा अन्वेषण सुविधाएं प्रदान करता है। हमने जेवीएम (जावा वर्चुअल मशीन) के शीर्ष पर चलने वाले कोटलिन v1.6 को प्रोग्रामिंग भाषा के रूप में, स्प्रिंग बूट 2.5.3 को फ्रेमवर्क के रूप में, और हाइबरनेट 5.4.32.फाइनल को ओआरएम (ऑब्जेक्ट रिलेशनल मैपिंग) के रूप में अपनाया। हमने इस प्रौद्योगिकी स्टैक को चुनने का मुख्य कारण यह है कि गति सबसे महत्वपूर्ण व्यावसायिक आवश्यकताओं में से एक है। इसलिए, हमें एक ऐसी तकनीक की आवश्यकता थी जो भारी बहु-थ्रेड प्रसंस्करण का लाभ उठा सके, और स्प्रिंग बूट एक विश्वसनीय समाधान निकला।
हमने अपने बैकएंड को डोक्कू द्वारा प्रबंधित एक डॉकर कंटेनर के माध्यम से 16GB 8CPU VPS पर तैनात किया है। इसमें ज्यादा से ज्यादा 15GB रैम का इस्तेमाल किया जा सकता है. ऐसा इसलिए है क्योंकि एक जीबी रैम रेडिस-आधारित कैशिंग सिस्टम को समर्पित है। हमने इसे प्रदर्शन में सुधार करने और बार-बार संचालन के साथ बैकएंड को ओवरलोड करने से बचने के लिए जोड़ा है।
डेटाबेस और टेबल संरचना
डेटाबेस के लिए, हमने MySQL 8 को चुनने का फैसला किया। एक 8GB और 2 CPU VPS वर्तमान में डेटाबेस सर्वर को होस्ट करता है, जो 200 समवर्ती कनेक्शन तक का समर्थन करता है। संचार ओवरहेड से बचने के लिए बैकएंड एप्लिकेशन और डेटाबेस एक ही सर्वर फ़ार्म में हैं। हमने दोहराव से बचने और प्रदर्शन को ध्यान में रखते हुए डेटाबेस संरचना तैयार की है। हमने एक रिलेशनल डेटाबेस अपनाने का फैसला किया क्योंकि हम प्रदाताओं से प्राप्त डेटा को परिवर्तित करने के लिए एक सुसंगत संरचना चाहते थे। इस तरह, हम खेल डेटा का मानकीकरण करते हैं, जिससे इसे एक्सप्लोर करना और इसे अंतिम उपयोगकर्ताओं के लिए प्रस्तुत करना आसान हो जाता है।
लेखन के समय डेटाबेस में सैकड़ों टेबल होते हैं, और एनडीए पर हस्ताक्षर किए जाने के कारण मैं उन सभी को प्रस्तुत नहीं कर सकता। सौभाग्य से, एक तालिका पूरी तरह से विश्लेषण करने के लिए पर्याप्त है कि हमने डेटा संदर्भ-आधारित विभाजन को क्यों अपनाया, जिसे आप देखने जा रहे हैं। असली चुनौती तब आई जब हमने इवेंट टेबल पर भारी क्वेरी करना शुरू किया। लेकिन उसमें गोता लगाने से पहले, आइए देखें कि इवेंट टेबल कैसा दिखता है:
जैसा कि आप देख सकते हैं, इसमें कई कॉलम शामिल नहीं हैं, लेकिन ध्यान रखें कि गोपनीयता कारणों से मुझे उनमें से कुछ को छोड़ना पड़ा। लेकिन क्या वास्तव में यहां महत्वपूर्ण हैं parameterId
और gameId
स्तंभ। हम इन दो विदेशी कुंजियों का उपयोग एक प्रकार के पैरामीटर (जैसे, लक्ष्य, पीला कार्ड, पास, पेनल्टी) और उन खेलों का चयन करने के लिए करते हैं जिनमें यह हुआ था।
प्रदर्शन समस्याएं
ईवेंट तालिका कुछ ही महीनों में आधा बिलियन पंक्तियों तक पहुंच गई। जैसा कि हम पहले ही इस ब्लॉग पोस्ट में गहराई से कवर कर चुके हैं, मुख्य समस्या यह है कि हमें धीमे IN प्रश्नों का उपयोग करके समग्र संचालन करने की आवश्यकता है। ऐसा इसलिए है क्योंकि खेल के दौरान क्या होता है यह इतना महत्वपूर्ण नहीं है। इसके बजाय, खेल विशेषज्ञ रुझान खोजने और उनके आधार पर निर्णय लेने के लिए एकत्रित डेटा का विश्लेषण करना चाहते हैं।
साथ ही, हालांकि वे आम तौर पर पूरे सीज़न या पिछले 5 या 10 गेम का विश्लेषण करते हैं, उपयोगकर्ता अक्सर कुछ विशेष गेम को अपने विश्लेषण से बाहर करना चाहते हैं। ऐसा इसलिए है क्योंकि वे नहीं चाहते कि उनके परिणामों का ध्रुवीकरण करने के लिए कोई खेल विशेष रूप से खराब या अच्छा खेला जाए। हम समग्र डेटा को प्री-जेनरेट नहीं कर सकते क्योंकि हमें इसे सभी संभावित संयोजनों पर करना होगा, जो संभव नहीं है। इसलिए, हमें सभी डेटा को स्टोर करना होगा और इसे तुरंत एकत्र करना होगा।
प्रदर्शन समस्या को समझना
अब, आइए केंद्रीय पहलू पर ध्यान दें, जिसके कारण हमें प्रदर्शन संबंधी समस्याओं का सामना करना पड़ा।
मिलियन-पंक्ति तालिकाएं धीमी होती हैं
यदि आपने कभी करोड़ों पंक्तियों वाली तालिकाओं का अध्ययन किया है, तो आप जानते हैं कि वे स्वाभाविक रूप से धीमी हैं। आप इतने बड़े टेबल पर जॉइन चलाने के बारे में सोच भी नहीं सकते। फिर भी, आप उचित समय में सेलेक्ट क्वेश्चन निष्पादित कर सकते हैं। यह विशेष रूप से सच है जब इन प्रश्नों में सरल WHERE शर्तें शामिल होती हैं। दूसरी ओर, समग्र कार्यों या IN क्लॉज का उपयोग करते समय वे बहुत धीमे हो जाते हैं। इन मामलों में, वे आसानी से 80 सेकंड तक का समय ले सकते हैं, जो कि बहुत अधिक है।
अनुक्रमणिका पर्याप्त नहीं हैं
प्रदर्शन को बेहतर बनाने के लिए, हमने कुछ इंडेक्स को परिभाषित करने का निर्णय लिया। प्रदर्शन के मुद्दों का समाधान खोजने के लिए यह हमारा पहला दृष्टिकोण था। लेकिन, दुर्भाग्य से, इससे एक और समस्या पैदा हो गई। सूचकांक समय और स्थान लेते हैं। यह आम तौर पर महत्वहीन है, लेकिन इतनी बड़ी तालिकाओं से निपटने के दौरान नहीं। यह पता चला कि सबसे सामान्य प्रश्नों के आधार पर जटिल अनुक्रमणिका को परिभाषित करने में कई घंटे और जीबी स्थान लगता है। साथ ही, अनुक्रमणिका सहायक तो हैं लेकिन जादू नहीं हैं।
समाधान के रूप में डेटा संदर्भ-आधारित डेटाबेस विभाजन
चूंकि हम कस्टम-डिफ़ाइंड इंडेक्स के साथ प्रदर्शन की समस्या को हल नहीं कर सके, इसलिए हमने एक नया तरीका आजमाने का फैसला किया। हमने अन्य विशेषज्ञों के साथ बात की, समाधानों के लिए ऑनलाइन खोज की, समान परिदृश्यों पर आधारित लेख पढ़े, और अंत में निर्णय लिया कि डेटाबेस को विभाजित करना अनुसरण करने का सही तरीका था।
पारंपरिक विभाजन सही दृष्टिकोण क्यों नहीं हो सकता है
अपनी सभी सबसे बड़ी तालिकाओं को विभाजित करने से पहले, हमने MySQL के आधिकारिक दस्तावेज़ीकरण और दिलचस्प लेखों दोनों में विषय का अध्ययन किया। यद्यपि हम सभी सहमत थे कि यह रास्ता तय करना था, हमने यह भी महसूस किया कि हमारे विशेष एप्लिकेशन डोमेन को ध्यान में रखे बिना विभाजन लागू करना एक गलती होगी। विशेष रूप से, हमने समझा कि डेटाबेस को विभाजित करते समय उचित मानदंड खोजना कितना महत्वपूर्ण था। विभाजन में कुछ विशेषज्ञों ने हमें सिखाया कि पारंपरिक दृष्टिकोण पंक्तियों की संख्या पर विभाजन करना है। लेकिन हम उससे अधिक बुद्धिमान और अधिक कुशल कुछ खोजना चाहते थे।
विभाजन मानदंड खोजने के लिए एप्लिकेशन डोमेन में जाना
हमने एप्लिकेशन डोमेन का विश्लेषण करके और अपने उपयोगकर्ताओं का साक्षात्कार करके एक आवश्यक सबक सीखा। खेल विशेषज्ञ एक ही प्रतियोगिता में खेलों से एकत्रित डेटा का विश्लेषण करते हैं। उदाहरण के लिए, सॉकर में एक प्रतियोगिता लीग, टूर्नामेंट या एकल मैच हो सकती है जहां आप ट्रॉफी जीत सकते हैं। हजारों अलग-अलग प्रतियोगिताएं हैं। यूरोप में सबसे महत्वपूर्ण चैंपियंस लीग, प्रीमियर लीग, लालिगा, सीरी ए, बुंडेसलिगा, इरेडिविसी, लीगा 1 और प्राइमिरा लीगा हैं।
इसका मतलब है कि हमारे उपयोगकर्ता विभिन्न प्रतियोगिताओं से आने वाले डेटा को बहुत कम ही ध्यान में रखते हैं। साथ ही, वे सीज़न दर सीज़न डेटा एक्सप्लोर करना पसंद करते हैं। दूसरे शब्दों में, वे शायद ही कभी किसी विशेष सीज़न में खेली गई खेल प्रतियोगिता द्वारा दर्शाए गए संदर्भ को छोड़ते हैं। हमारी डेटाबेस संरचना ने इस अवधारणा को SeasonCompetition
. नामक तालिका के साथ व्यक्त किया है , जिसका लक्ष्य किसी प्रतियोगिता को एक विशिष्ट सीज़न के साथ जोड़ना है। इसलिए, हमने महसूस किया कि एक अच्छा तरीका यह होगा कि हम अपनी बड़ी तालिकाओं को किसी विशेष SeasonCompetition
से संबंधित उप-तालिकाओं में विभाजित करें। उदाहरण।
विशेष रूप से, हमने इन नई तालिकाओं के लिए निम्नलिखित नाम प्रारूप को परिभाषित किया है:<tableName>_<seasonCompetitionId>
।
नतीजतन, अगर हमारे पास SeasonCompetition
. में 100 पंक्तियाँ थीं तालिका में, हमें बड़े Events
को विभाजित करना होगा तालिका को छोटे Events_1
. में बदलें , Events_2
, ..., Events_100
टेबल। हमारे विश्लेषण के आधार पर, इस दृष्टिकोण से औसत मामले में काफी प्रदर्शन में वृद्धि होगी, हालांकि दुर्लभ मामलों में कुछ ओवरहेड पेश करना।
मानदंड का सबसे सामान्य प्रश्नों से मिलान करना
इस जटिल और संभावित रिटर्नलेस ऑपरेशन को निष्पादित करने के लिए स्क्रिप्ट को कोडिंग और लॉन्च करने से पहले, हमने अपने बैकएंड एप्लिकेशन द्वारा किए गए सबसे सामान्य प्रश्नों को देखकर अपने अध्ययन को मान्य किया। लेकिन ऐसा करते हुए, हमने पाया कि अधिकांश प्रश्नों में केवल सीज़न कॉम्पिटिशन के भीतर खेले जाने वाले गेम शामिल थे। इससे हमें यकीन हो गया कि हम सही थे। इसलिए हमने डेटाबेस में सभी बड़ी तालिकाओं को केवल परिभाषित दृष्टिकोण के साथ विभाजित किया।
SELECT AVG('value') as 'value', SUM('minutes') as 'minutes'
FROM 'Events'
WHERE 'parameterId' = 15 AND 'gameId' IN(223,241,245,212,201,299,187,304,187,205)
GROUP BY 'teamId'
अब, आइए इस निर्णय के पेशेवरों और विपक्षों का अध्ययन करें।
पेशेवर
- अधिकतम आधा मिलियन पंक्तियों वाली तालिका पर क्वेरी चलाना आधा बिलियन पंक्तियों वाली तालिका पर करने की तुलना में बहुत अधिक प्रदर्शनकारी है, विशेष रूप से जब प्रश्नों को एकत्रित करने की बात आती है।
- छोटी तालिकाओं को प्रबंधित करना और अपडेट करना आसान होता है। किसी कॉलम या इंडेक्स को जोड़ना समय और स्थान के मामले में पहले की तुलना में भी नहीं है। साथ ही, प्रत्येक
SeasonCompetition
अलग है और विभिन्न विश्लेषणों की आवश्यकता है। नतीजतन, इसके लिए विशेष कॉलम और इंडेक्स की आवश्यकता हो सकती है, और उपरोक्त विभाजन हमें इससे आसानी से निपटने की अनुमति देता है। - प्रदाता कुछ डेटा में संशोधन कर सकता है। यह हमें डिलीट और अपडेट क्वेश्चन करने के लिए मजबूर करता है, जो ऐसी छोटी टेबल पर असीम रूप से तेज होते हैं। साथ ही, वे हमेशा किसी विशेष
SeasonCompetition
. के केवल कुछ खेलों की चिंता करते हैं , इसलिए हमें अब केवल एक ही टेबल पर काम करने की जरूरत है।
विपक्ष
- इन उप-तालिकाओं पर कोई प्रश्न पूछने से पहले, हमें
seasonCompetitionId
को जानना होगा रुचि के खेल से जुड़ा हुआ है। ऐसा इसलिए है क्योंकिseasonCompetitionId
मान तालिका नाम में प्रयोग किया जाता है। इसलिए, हमारे बैकएंड को विश्लेषण में गेम देखकर क्वेरी चलाने से पहले इस जानकारी को पुनः प्राप्त करने की आवश्यकता है, जो एक छोटे से ओवरहेड का प्रतिनिधित्व करता है। - जब किसी क्वेरी में गेम का एक सेट शामिल होता है जिसमें कई
SeasonCompetitions
शामिल होती हैं , बैकएंड एप्लिकेशन को प्रत्येक उप-तालिका पर एक क्वेरी चलानी चाहिए। इसलिए, इन मामलों में, हम अब डेटाबेस स्तर पर डेटा एकत्र नहीं कर सकते हैं, और हमें इसे एप्लिकेशन स्तर पर करना चाहिए। यह बैकएंड तर्क में कुछ जटिलता का परिचय देता है। उसी समय, हम इन प्रश्नों को समानांतर में निष्पादित कर सकते हैं। साथ ही, हम पुनर्प्राप्त किए गए डेटा को कुशलतापूर्वक और समानांतर में एकत्रित कर सकते हैं। - हजारों टेबल वाले डेटाबेस को मैनेज करना आसान नहीं है और क्लाइंट में एक्सप्लोर करना चुनौतीपूर्ण हो सकता है। इसी तरह, प्रत्येक तालिका में एक नया कॉलम जोड़ना या मौजूदा कॉलम को अपडेट करना बोझिल है और इसके लिए एक कस्टम स्क्रिप्ट की आवश्यकता होती है।
प्रदर्शन पर डेटा संदर्भ-आधारित विभाजन के प्रभाव
आइए अब नए विभाजित डेटाबेस में एक क्वेरी निष्पादित करते समय प्राप्त किए गए समय सुधार को देखें।
- औसत मामले में समय में सुधार (केवल एक
SeasonCompetition
वाली क्वेरी) ):20x से 40x तक - सामान्य स्थिति में समय में सुधार (एक या अधिक
SeasonCompetitions
वाली क्वेरी) ):5x से 10x तक
अंतिम विचार
अपने डेटाबेस को विभाजित करना निस्संदेह प्रदर्शन में सुधार करने का एक शानदार तरीका है, खासकर बड़े डेटाबेस पर। हालांकि, अपने विशेष एप्लिकेशन डोमेन पर विचार किए बिना ऐसा करना एक गलती हो सकती है या एक अक्षम समाधान हो सकता है। इसके बजाय, विशेषज्ञों और अपने उपयोगकर्ताओं का साक्षात्कार करके और सबसे अधिक निष्पादित प्रश्नों को देखकर डोमेन का अध्ययन करने के लिए अपना समय निकालना अत्यधिक कुशल विभाजन मानदंड की कल्पना करना महत्वपूर्ण है। इस लेख ने आपको दिखाया कि यह कैसे करना है और वास्तविक दुनिया के मामले के अध्ययन के माध्यम से इस तरह के दृष्टिकोण के परिणामों का प्रदर्शन किया।