1.1 शार्डिंग क्या है?

यदि आप लगातार Google करते हैं, तो यह पता चला है कि तथाकथित विभाजन और तथाकथित विखंडन के बीच एक धुंधली सीमा है। हर कोई जो चाहे, जो चाहे कह दे। कुछ लोग क्षैतिज विभाजन और विखंडन के बीच अंतर करते हैं। दूसरों का कहना है कि विखंडन एक निश्चित प्रकार का क्षैतिज विभाजन है।

मुझे एक भी पारिभाषिक मानक नहीं मिला जो संस्थापक पिताओं द्वारा अनुमोदित हो और आईएसओ द्वारा प्रमाणित हो। व्यक्तिगत आंतरिक विश्वास कुछ इस तरह है: मनमाने ढंग से लिए गए तरीके से औसतन विभाजन "आधार को टुकड़ों में काटना" है।

  • लंबवत विभाजन - कॉलम द्वारा। उदाहरण के लिए, 60 कॉलम में कुछ अरब रिकॉर्ड वाली एक विशाल तालिका है। ऐसी एक विशाल तालिका रखने के बजाय, हम कम से कम 2 बिलियन रिकॉर्ड की 60 विशाल तालिकाएँ रखते हैं - और यह एक स्तंभ आधार नहीं है, बल्कि ऊर्ध्वाधर विभाजन (शब्दावली के उदाहरण के रूप में) है।
  • क्षैतिज विभाजन - हम लाइन से लाइन काटते हैं, शायद सर्वर के अंदर।

यहाँ अजीब क्षण क्षैतिज विभाजन और विखंडन के बीच का सूक्ष्म अंतर है। मुझे टुकड़ों में काटा जा सकता है, लेकिन मैं आपको निश्चित रूप से नहीं बता सकता कि यह क्या है। ऐसा लगता है कि विखंडन और क्षैतिज विभाजन एक ही चीज़ के बारे में हैं।

साझाकरण , सामान्य रूप से, जब डेटाबेस के संदर्भ में एक बड़ी तालिका या दस्तावेजों, वस्तुओं का एक प्रो-संग्रह, यदि आपके पास डेटाबेस नहीं है, लेकिन एक दस्तावेज़ स्टोर है, तो वस्तुओं द्वारा सटीक रूप से काटा जाता है। यानी, 2 अरब वस्तुओं से, टुकड़ों का चयन किया जाता है चाहे आकार कोई भी हो। प्रत्येक वस्तु के अंदर की वस्तुओं को टुकड़ों में नहीं काटा जाता है, हम उन्हें अलग-अलग स्तंभों में नहीं रखते हैं, अर्थात्, हम उन्हें अलग-अलग स्थानों पर बैचों में रखते हैं।

सूक्ष्म पारिभाषिक अंतर हैं। उदाहरण के लिए, अपेक्षाकृत बोलते हुए, पोस्टग्रेज़ डेवलपर्स कह सकते हैं कि क्षैतिज विभाजन तब होता है जब सभी तालिकाएँ जिनमें मुख्य तालिका विभाजित होती है, एक ही स्कीमा में होती हैं, और जब विभिन्न मशीनों पर होती हैं, तो यह पहले से ही शार्डिंग होती है।

एक सामान्य अर्थ में, एक विशिष्ट डेटाबेस और एक विशिष्ट डेटा प्रबंधन प्रणाली की शब्दावली से बंधे बिना, एक भावना है कि शार्पिंग केवल दस्तावेज़ द्वारा लाइन / दस्तावेज़ द्वारा स्लाइसिंग लाइन है, और इसी तरह - यह सब है।

मैं विशिष्ट पर जोर देता हूं। इस अर्थ में कि हम यह सब केवल 2 बिलियन दस्तावेजों को 20 तालिकाओं में काटने के लिए नहीं कर रहे हैं, जिनमें से प्रत्येक अधिक प्रबंधनीय होगा, बल्कि इसे कई कोर, कई डिस्क या कई अलग-अलग भौतिक या आभासी सर्वरों पर वितरित करने के लिए कर रहे हैं।

1.2 अविभाज्य को विभाजित करें

यह समझा जाता है कि हम ऐसा इसलिए करते हैं ताकि प्रत्येक शार्ड - डेटा का प्रत्येक टुकड़ा - कई बार दोहराया जाए। लेकिन वास्तव में, नहीं।

INSERT INTO docs00 
SELECT * FROM documents WHERE (id%16)=0 
... 
 
INSERT INTO docs15 
SELECT * FROM documents WHERE (id%16)=15 

वास्तव में, यदि आप डेटा का ऐसा टुकड़ा करते हैं, और अपने बहादुर लैपटॉप पर MySQL पर एक विशाल SQL तालिका से, आप 16 छोटी तालिकाएँ उत्पन्न करेंगे, बिना एक लैपटॉप से ​​​​परे, एक भी स्कीमा नहीं, एक भी डेटाबेस नहीं, आदि। . और इसी तरह। - बस इतना ही, आपके पास पहले से ही शार्डिंग है।

इसका परिणाम निम्नलिखित होता है:

  • बैंडविड्थ बढ़ जाती है।
  • विलंबता नहीं बदलती है, यानी प्रत्येक, बोलने के लिए, कार्यकर्ता या उपभोक्ता इस मामले में अपना खुद का हो जाता है। लगभग एक ही समय में अलग-अलग अनुरोध किए जाते हैं।
  • या दोनों, और दूसरा, और उच्च उपलब्धता (प्रतिकृति) भी।

बैंडविड्थ क्यों? हमारे पास कभी-कभी डेटा की ऐसी मात्रा हो सकती है जो फिट नहीं होती - यह स्पष्ट नहीं है कि कहाँ, लेकिन वे फिट नहीं होती हैं - 1 {कर्नेल | डिस्क | सर्वर | ...}। पर्याप्त संसाधन नहीं हैं, बस इतना ही। इस बड़े डेटासेट के साथ काम करने के लिए, आपको इसे काटने की जरूरत है।

विलंबता क्यों? एक कोर पर, 2 बिलियन पंक्तियों की तालिका को स्कैन करना 20 कोर पर 20 तालिकाओं को स्कैन करने की तुलना में 20 गुना धीमा है, इसे समानांतर में कर रहा है। एकल संसाधन पर डेटा बहुत धीरे-धीरे संसाधित होता है।

उच्च उपलब्धता क्यों? या हम एक ही समय में दोनों करने के लिए डेटा को काटते हैं, और एक ही समय में प्रत्येक शार्ड की कई प्रतियां - प्रतिकृति उच्च उपलब्धता सुनिश्चित करती है।

1.3 एक सरल उदाहरण "इसे हाथ से कैसे करें"

32 दस्तावेज़ों के लिए परीक्षण तालिका test.documents का उपयोग करके सशर्त शार्डिंग को काटा जा सकता है, और इस तालिका से 16 परीक्षण तालिकाओं का निर्माण किया जा सकता है, लगभग 2 दस्तावेज़ प्रत्येक test.docs00, 01, 02, ..., 15।

INSERT INTO docs00 
SELECT * FROM documents WHERE (id%16)=0 
... 
 
INSERT INTO docs15 
SELECT * FROM documents WHERE (id%16)=15 

के बारे में क्यों? क्योंकि एक प्राथमिकता हम नहीं जानते कि आईडी कैसे वितरित की जाती है, यदि 1 से 32 समावेशी हैं, तो प्रत्येक में ठीक 2 दस्तावेज़ होंगे, अन्यथा नहीं।

हम इसे यहाँ क्यों करते हैं। 16 टेबल बनाने के बाद, हमें जो चाहिए उसमें से 16 को "हड़प" सकते हैं। हम जो भी हिट करते हैं, उसके बावजूद हम इन संसाधनों को समानांतर कर सकते हैं। उदाहरण के लिए, यदि पर्याप्त डिस्क स्थान नहीं है, तो इन तालिकाओं को अलग-अलग डिस्क पर विघटित करना समझदारी होगी।

यह सब, दुर्भाग्य से, मुफ़्त नहीं है। मुझे संदेह है कि विहित SQL मानक के मामले में (मैंने लंबे समय तक SQL मानक को फिर से नहीं पढ़ा है, शायद इसे लंबे समय तक अपडेट नहीं किया गया है), किसी भी SQL सर्वर को कहने के लिए कोई आधिकारिक मानकीकृत सिंटैक्स नहीं है : "प्रिय SQL सर्वर, मुझे 32 शार्क बनाएं और उन्हें 4 डिस्क में विभाजित करें। लेकिन व्यक्तिगत कार्यान्वयन में, मूल रूप से समान कार्य करने के लिए अक्सर एक विशिष्ट सिंटैक्स होता है। PostgreSQL में विभाजन के लिए तंत्र है, MySQL में MariaDB है, Oracle ने शायद यह सब बहुत पहले किया था।

फिर भी, यदि हम इसे डेटाबेस समर्थन के बिना और मानक के ढांचे के भीतर हाथ से करते हैं, तो हम डेटा एक्सेस की जटिलता के साथ सशर्त भुगतान करते हैं । जहां दस्तावेज़ों से एक साधारण चयन था * जहां आईडी = 123, अब 16 x चयन * डॉक्सएक्सएक्स से। और यह अच्छा है अगर हम कुंजी द्वारा रिकॉर्ड प्राप्त करने का प्रयास करें। अगर हम रिकॉर्ड की शुरुआती रेंज हासिल करने की कोशिश कर रहे हैं तो यह और भी दिलचस्प है। अब (यदि हम जोर देते हैं, जैसे कि मूर्ख हैं, और मानक के ढांचे के भीतर रहते हैं), तो इन 16 SELECT * FROM के परिणामों को आवेदन में जोड़ना होगा।

आप किस प्रदर्शन परिवर्तन की अपेक्षा कर सकते हैं?

  • सहज रूप से - रैखिक।
  • सैद्धांतिक रूप से - सबलाइनियर, क्योंकि Amdahl कानून।
  • व्यावहारिक रूप से, शायद लगभग रैखिक रूप से, शायद नहीं।

वास्तव में, सही उत्तर अज्ञात है। शार्डिंग तकनीक के एक चतुर अनुप्रयोग के साथ, आप अपने आवेदन के प्रदर्शन में एक महत्वपूर्ण सुपर-लीनियर गिरावट प्राप्त कर सकते हैं, और यहां तक ​​कि डीबीए भी एक लाल-गर्म पोकर के साथ चलेगा।

आइए देखें कि यह कैसे हासिल किया जा सकता है। यह स्पष्ट है कि बस सेटिंग को PostgreSQL shards = 16 पर सेट करना, और फिर यह अपने आप बंद हो जाता है, दिलचस्प नहीं है। आइए इस बारे में सोचें कि हम यह कैसे सुनिश्चित कर सकते हैं कि हम 16 गुणा 32 से धीमा हो जाएं - यह इस दृष्टिकोण से दिलचस्प है कि यह कैसे नहीं करना है।

गति बढ़ाने या धीमा करने के हमारे प्रयास हमेशा क्लासिक्स में चलेंगे - अच्छे पुराने अमदहल कानून, जो कहते हैं कि किसी भी अनुरोध का कोई पूर्ण समानांतरकरण नहीं होता है, हमेशा कुछ सुसंगत हिस्सा होता है।

1.4 अमदहल कानून

हमेशा एक क्रमबद्ध भाग होता है।

क्वेरी निष्पादन का हमेशा एक हिस्सा होता है जो समानांतर होता है, और हमेशा एक हिस्सा होता है जो समानांतर नहीं होता है। यहां तक ​​​​कि अगर यह आपको लगता है कि एक पूरी तरह से समानांतर क्वेरी है, तो कम से कम परिणाम पंक्ति का संग्रह जिसे आप ग्राहक को प्रत्येक शार्क से प्राप्त पंक्तियों से भेजने जा रहे हैं, और यह हमेशा अनुक्रमिक है।

हमेशा कुछ सुसंगत हिस्सा होता है। यह सामान्य पृष्ठभूमि के खिलाफ छोटा, पूरी तरह से अदृश्य हो सकता है, यह विशाल हो सकता है और तदनुसार, समानांतरता को दृढ़ता से प्रभावित करता है, लेकिन यह हमेशा मौजूद रहता है।

इसके अलावा, इसका प्रभाव बदल रहा है और महत्वपूर्ण रूप से बढ़ सकता है, उदाहरण के लिए, यदि हम अपनी टेबल को काटते हैं - चलिए हिस्सेदारी बढ़ाते हैं - 64 रिकॉर्ड से 4 रिकॉर्ड की 16 टेबल में, यह हिस्सा बदल जाएगा। बेशक, इतनी बड़ी मात्रा में डेटा को देखते हुए, हम एक मोबाइल फोन और 2 मेगाहर्ट्ज 86 प्रोसेसर पर काम कर रहे हैं, और हमारे पास पर्याप्त फाइलें नहीं हैं जिन्हें एक ही समय में खुला रखा जा सके। जाहिर है, ऐसे इनपुट के साथ, हम एक बार में एक फाइल खोलते हैं।

  • यह Total = Serial + Parallel था । जहां, उदाहरण के लिए, समानांतर डीबी के अंदर सभी काम है, और सीरियल क्लाइंट को परिणाम भेज रहा है।
  • बन गया Total2 = Serial + Parallel/N + Xserial । उदाहरण के लिए, जब कुल मिलाकर ORDER BY, Xserial>0.

इस सरल उदाहरण के साथ, मैं यह दिखाने की कोशिश कर रहा हूं कि कुछ एक्ससीरियल दिखाई देता है। इस तथ्य के अलावा कि हमेशा एक क्रमबद्ध भाग होता है, और यह तथ्य कि हम समानांतर में डेटा के साथ काम करने की कोशिश कर रहे हैं, इस डेटा स्लाइसिंग को प्रदान करने के लिए एक अतिरिक्त भाग है। मोटे तौर पर, हमें इसकी आवश्यकता हो सकती है:

  • डेटाबेस के आंतरिक शब्दकोश में इन 16 तालिकाओं को खोजें;
  • खुली फ़ाइलें;
  • मेमोरी आवंटित करें;
  • मेमोरी को अनलॉक करें;
  • मर्ज परिणाम;
  • कोर के बीच सिंक्रनाइज़ करें।

कुछ आउट-ऑफ़-सिंक प्रभाव अभी भी दिखाई देते हैं। वे नगण्य हो सकते हैं और कुल समय के एक अरबवें हिस्से पर कब्जा कर सकते हैं, लेकिन वे हमेशा शून्य नहीं होते हैं और हमेशा वहां रहते हैं। उनकी मदद से, हम शार्किंग के बाद नाटकीय रूप से प्रदर्शन खो सकते हैं।

यह Amdahl's law के बारे में एक मानक तस्वीर है। यहाँ महत्वपूर्ण बात यह है कि रेखाएँ, जो आदर्श रूप से सीधी होनी चाहिए और रैखिक रूप से विकसित होनी चाहिए, एक स्पर्शोन्मुख में चलती हैं। लेकिन चूंकि इंटरनेट से ग्राफ अपठनीय है, मैंने अपनी राय में, संख्याओं के साथ अधिक दृश्य तालिकाएँ बनाईं।

मान लीजिए कि हमारे पास अनुरोध प्रसंस्करण का कुछ क्रमबद्ध हिस्सा है जो केवल 5% लेता है: सीरियल = 0.05 = 1/20

सहज रूप से, ऐसा लगता है कि एक क्रमबद्ध भाग के साथ जो अनुरोध प्रसंस्करण का केवल 1/20 लेता है, अगर हम 20 कोर के लिए अनुरोध प्रसंस्करण को समानांतर करते हैं, तो यह लगभग 20 हो जाएगा, सबसे खराब स्थिति में 18 गुना तेज।

वास्तव में, गणित एक हृदयहीन चीज है :

वॉल = 0.05 + 0.95/num_cores, स्पीडअप = 1 / (0.05 + 0.95/num_cores)

यह पता चला है कि यदि आप सावधानीपूर्वक गणना करते हैं, तो 5% के क्रमबद्ध भाग के साथ, स्पीडअप 10 गुना (10.3) होगा, जो सैद्धांतिक आदर्श की तुलना में 51% है।

8 कोर = 5.9 = 74%
10 कोर = 6.9 = 69%
20 कोर = 10.3 = 51%
40 कोर = 13.6 = 34%
128 कोर = 17.4 = 14%

उस कार्य के लिए 20 कोर (20 डिस्क, यदि आप चाहें तो) का उपयोग करने के बाद, हम कभी भी सैद्धांतिक रूप से 20 गुना से अधिक का त्वरण प्राप्त नहीं करेंगे, लेकिन व्यवहार में - बहुत कम। इसके अलावा, समानांतरों की संख्या में वृद्धि के साथ अक्षमता बहुत बढ़ जाती है।

जब क्रमबद्ध कार्य का केवल 1% शेष रहता है, और 99% समानांतर होता है, तो स्पीडअप मान में कुछ सुधार होता है:

8 कोर = 7.5 = 93%
16 कोर = 13.9 = 87%
32 कोर = 24.4 = 76%
64 कोर = 39.3 = 61%

पूरी तरह से थर्मोन्यूक्लियर क्वेरी के लिए, जिसे पूरा करने में स्वाभाविक रूप से घंटों लगते हैं, और प्रारंभिक कार्य और परिणाम की असेंबली में बहुत कम समय लगता है (सीरियल = 0.001), हम पहले से ही अच्छी दक्षता देखेंगे:

8 कोर = 7.94 = 99%
16 कोर = 15.76 = 99%
32 कोर = 31.04 = 97%
64 कोर = 60.20 = 94%

कृपया ध्यान दें कि हम कभी भी 100% नहीं देखेंगे । विशेष रूप से अच्छे मामलों में, आप देख सकते हैं, उदाहरण के लिए, 99.999%, लेकिन ठीक 100% नहीं।