चर लंबाई के फ़ील्ड के लिए डेटाबेस प्रमुख सूचकांक मूल्यों (ऑन-डिस्क) को कैसे संग्रहीत करते हैं?


16

प्रसंग

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

पृष्ठभूमि

SQL (जैसे MySQL) और NoSQL (CouchDB, MongoDB, आदि) डेटाबेस में, जब आप किसी कॉलम या JSON डॉक्यूमेंट फील्ड ऑफ़ डेटा पर एक इंडेक्स का निर्माण करते हैं, तो आप वास्तव में जो डेटाबेस बनाने का कारण बनते हैं, वह अनिवार्य रूप से सभी की क्रमबद्ध सूची बनाता है फ़ाइल के साथ वे मान मुख्य डेटा फ़ाइल में ऑफसेट होते हैं, जहाँ उस मान से संबंधित रिकॉर्ड रहता है।

(सादगी की खातिर, मैं हाथ से अन्य विशिष्ट विवरणों के गूढ़ विवरण को मिटा सकता हूं)

सरल क्लासिक एसक्यूएल उदाहरण

एक मानक एसक्यूएल टेबल पर विचार करें जिसमें एक साधारण 32-बिट इंट प्राथमिक कुंजी है जिसे हम एक इंडेक्स बनाते हैं, हम पूर्णांक की-डिस्क के एक इंडेक्स के साथ समाप्त हो जाएंगे, जिसे सॉर्ट किया गया है और डेटा फ़ाइल में 64-बिट ऑफ़सेट के साथ संबद्ध है जहां रिकॉर्ड रहता है, उदाहरण के लिए:

id   | offset
--------------
1    | 1375
2    | 1413
3    | 1786

सूचकांक में कुंजियों का ऑन-डिस्क प्रतिनिधित्व कुछ इस तरह दिखता है:

[4-bytes][8-bytes] --> 12 bytes for each indexed value

फाइलसिस्टम और डेटाबेस सिस्टम के साथ डिस्क I / O को अनुकूलित करने के बारे में अंगूठे के मानक नियमों के अनुसार, चलिए आपको डिस्क पर 4KB ब्लॉक में कुंजियों को संग्रहीत करने का मतलब है:

4096 bytes / 12 bytes per key = 341 keys per block

सूचकांक (बी + ट्री, हैश, सॉर्ट की गई सूची, आदि) की समग्र संरचना को अनदेखा करते हुए हम 341 कुंजी के ब्लॉक को एक बार में मेमोरी में पढ़ते हैं और आवश्यकतानुसार डिस्क पर वापस भेजते हैं।

उदाहरण क्वेरी

पिछले अनुभाग से जानकारी का उपयोग करते हुए, मान लें कि "id = 2" के लिए एक क्वेरी आती है, क्लासिक DB इंडेक्स लुकअप निम्नानुसार है:

  1. सूचकांक की जड़ पढ़ें (इस मामले में, 1 ब्लॉक)
  2. बाइनरी-कुंजी को खोजने के लिए सॉर्ट किए गए ब्लॉक को खोजें
  3. मान से डेटा फ़ाइल ऑफ़सेट प्राप्त करें
  4. ऑफसेट का उपयोग करके डेटा फ़ाइल में रिकॉर्ड देखें
  5. कॉलर को डेटा लौटाएं

प्रश्न सेटअप ...

ठीक है, यहाँ है जहाँ सवाल एक साथ आता है ...

चरण # 2 सबसे महत्वपूर्ण हिस्सा है जो इन प्रश्नों को O (logn) समय में निष्पादित करने की अनुमति देता है ... सूचना को क्रमबद्ध किया जाना चाहिए, लेकिन आपको त्वरित-क्रमबद्ध तरीके से सूची का पता लगाने में सक्षम होना चाहिए ... और विशेष रूप से, आपको उस स्थिति में सूचकांक कुंजी मूल्य में पढ़ने के लिए वसीयत में अच्छी तरह से परिभाषित ऑफसेट पर कूदने में सक्षम होना चाहिए।

ब्लॉक में पढ़ने के बाद, आपको तुरंत 170 वें स्थान पर कूदने में सक्षम होना चाहिए, मुख्य मूल्य पढ़ें और देखें कि क्या आप जिस जीटी या एलटी की तलाश कर रहे हैं, वह स्थिति (और इसी तरह और इसी तरह ...)

एकमात्र तरीका है कि आप ब्लॉक में डेटा के चारों ओर कूदने में सक्षम होंगे जैसे कि यदि मुख्य मान आकार सभी अच्छी तरह से परिभाषित किए गए थे, जैसे हमारे उदाहरण ऊपर (4-बाइट्स फिर 8-बाइट्स प्रति कुंजी)।

सवाल

ठीक है, तो यहाँ है जहाँ मैं कुशल सूचकांक डिजाइन के साथ फंस रहा हूँ ... SQL डेटाबेस में varchar स्तंभों के लिए या अधिक विशेष रूप से, CouchDB या NoSQL जैसे दस्तावेज़ डेटाबेस में पूरी तरह से मुक्त-फ़ॉर्म फ़ील्ड, जहाँ आप जिस भी क्षेत्र को अनुक्रमित करना चाहते हैं, वह कोई भी हो सकता है लंबाई आप उन प्रमुख मूल्यों को कैसे लागू करते हैं जो सूचकांक संरचना के ब्लॉक के अंदर हैं जो आप अपने सूचकांक का निर्माण करते हैं?

उदाहरण के लिए, मान लें कि आप CouchDB में एक ID के लिए एक अनुक्रमिक काउंटर का उपयोग करते हैं और आप ट्वीट्स को अनुक्रमित कर रहे हैं ... आपके पास कुछ महीने बाद "मान" "1" से "100,000,000,000" तक जाएंगे।

मान लीजिए कि आप 1 दिन डेटाबेस पर इंडेक्स बनाते हैं, जब डेटाबेस में केवल 4 ट्वीट होते हैं, CouchDB इंडेक्स ब्लॉकों के अंदर प्रमुख मूल्यों के लिए निम्न निर्माण का उपयोग करने के लिए लुभा सकता है:

[1-byte][8-bytes] <-- 9 bytes
4096 / 9 = 455 keys per block

कुछ बिंदु पर यह टूट जाता है और आपको इंडेक्स में अपने प्रमुख मूल्य को संग्रहीत करने के लिए बाइट्स की एक चर संख्या की आवश्यकता होती है।

यदि आप एक "कलरवेज़_मैसेज" या कुछ और जैसे वास्तव में परिवर्तनीय-लंबाई क्षेत्र को अनुक्रमित करने का निर्णय लेते हैं, तो यह बिंदु और भी अधिक आकर्षक है।

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

जाहिर है अगर आपकी चाबियां परिवर्तनशील आकार की हैं और आप कुंजियों के ब्लॉक में पढ़ते हैं, तो न केवल आपको पता है कि ब्लॉक में वास्तव में कितनी चाबियां हैं , लेकिन आपको पता नहीं है कि बाइनरी करने के लिए सूची के बीच में कैसे कूदें उन पर खोज करें।

यह वह जगह है जहाँ मैं सब मिल रहा है।

क्लासिक एसक्यूएल डेटाबेस (जैसे बूल, इंट, चार, इत्यादि) में स्थिर-टाइप किए गए फ़ील्ड के साथ, मैं समझता हूं कि इंडेक्स प्रमुख लंबाई को पूर्व-परिभाषित कर सकता है और उससे चिपका रह सकता है ... लेकिन दस्तावेज़ डेटा स्टोर की इस दुनिया में, मैं हूं हैरान हैं कि वे डिस्क पर इस डेटा को कैसे कुशलता से मॉडलिंग कर रहे हैं ताकि यह अभी भी O (logn) समय में स्कैन किया जा सके और यहां किसी भी स्पष्टीकरण की सराहना की जा सके।

कृपया मुझे बताएं कि क्या किसी स्पष्टीकरण की आवश्यकता है!

अपडेट (ग्रेग का जवाब)

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

मैंने 3 अलग-अलग DBMS कार्यान्वयन (CouchDB, kivaloo और InnoDB) में देखा है और उनमें से सभी ने अपने निष्पादन वातावरण (erlang / C) के अंदर मूल्यों की खोज करने से पहले पूरे ब्लॉक को आंतरिक डेटा संरचना में deserializing द्वारा इस मुद्दे को संभालते हैं।

यह मुझे लगता है कि ग्रेग के सुझाव के बारे में बहुत शानदार है; 2048 के एक सामान्य ब्लॉक का आकार सामान्य रूप से 50 या उससे कम होगा, जिसके परिणामस्वरूप बहुत कम संख्या में ब्लॉक को पढ़ने की आवश्यकता होगी।

अद्यतन (संभावित सुझाव ग्रेग के सुझाव के लिए)

अपने आप से इस संवाद को सर्वोत्तम रूप से जारी रखने के लिए, मुझे निम्नलिखित बातों का एहसास हुआ ...

  1. यदि हर "ब्लॉक" ऑफसेट डेटा के साथ होता है, तो आप ब्लॉक आकार को बाद में सड़क के नीचे कॉन्फ़िगरेशन में समायोजित करने की अनुमति नहीं दे सकते हैं क्योंकि आप डेटा को पढ़ने में समाप्त हो सकते हैं जो हेडर के साथ सही तरीके से शुरू नहीं हुआ था या ब्लॉक नहीं था जिसमें कई हेडर थे।

  2. यदि आप विशाल कुंजी मानों को अनुक्रमित कर रहे हैं (कहते हैं कि कोई व्यक्ति चार (8192) या बूँद (8192) के स्तंभ को अनुक्रमणित करने का प्रयास कर रहा है) तो संभव है कि कुंजियाँ एक खंड में फिट न हों और दो खंडों के पार बहने की आवश्यकता हो । इसका मतलब है कि आपके पहले ब्लॉक में एक ऑफसेट हेडर होगा और दूसरा ब्लॉक तुरंत प्रमुख डेटा के साथ शुरू होगा।

इस सब का समाधान एक निश्चित डेटाबेस ब्लॉक आकार है, जो समायोज्य नहीं है और इसके चारों ओर हेडर ब्लॉक डेटा संरचनाएं विकसित कर रहा है ... उदाहरण के लिए, आप सभी ब्लॉक आकार को 4KB (आमतौर पर सबसे अधिक अनुकूलतम) को ठीक करते हैं और एक बहुत छोटा लिखते हैं ब्लॉक हेडर जिसमें शुरुआत में "ब्लॉक टाइप" शामिल है। यदि इसका सामान्य ब्लॉक है, तो ब्लॉक हेडर के तुरंत बाद ऑफसेट हेडर होना चाहिए। यदि इसका "अतिप्रवाह" प्रकार है, तो ब्लॉक हेडर कच्चे कुंजी डेटा के तुरंत बाद है।

अद्यतन (संभावित भयानक अप साइड)

ब्लॉक को बाइट्स की एक श्रृंखला के रूप में पढ़ा जाता है और ऑफसेट डिकोड हो जाता है; तकनीकी रूप से आप बस उस कुंजी को एनकोड कर सकते हैं जिसे आप कच्चे बाइट्स के लिए खोज रहे हैं और फिर बाइट स्ट्रीम पर सीधी तुलना करते हैं।

एक बार जिस कुंजी को आप ढूंढ रहे हैं वह मिल जाने के बाद, पॉइंटर को डिकोड किया जा सकता है और उसका अनुसरण किया जा सकता है।

ग्रेग के विचार का एक और भयानक पक्ष-प्रभाव! यहां सीपीयू टाइम ऑप्टिमाइजेशन की क्षमता काफी बड़ी है कि एक निश्चित ब्लॉक साइज सेट करना इस सब को हासिल करने के लिए इसके लायक हो सकता है।


इस विषय में रुचि रखने वाले किसी अन्य व्यक्ति के लिए, रेडिस के प्रमुख डिक्शन "डिस्क स्टोर" घटक को लागू करने की कोशिश करते हुए रेडिस के प्रमुख देव इस सटीक मुद्दे पर चल रहे थे। उन्होंने मूल रूप से 32-बाइट्स के "बड़े पर्याप्त" स्थिर कुंजी आकार का विकल्प चुना, लेकिन समस्याओं के लिए क्षमता का एहसास किया और इसके बजाय केवल एक सुसंगत आकार रखने के लिए चाबियाँ (sha1 या md5) के हैश को संग्रहीत करने का विकल्प चुना। यह राउंडेड क्वेरी करने की क्षमता को मारता है, लेकिन यह पेड़ को अच्छी तरह से एफडब्ल्यूआईडब्ल्यू को संतुलित करता है। यहाँ विवरण redis.hackyhack.net/2011-01-12.html
रियाद कल्ला

कुछ और जानकारी मुझे मिली। ऐसा लगता है कि SQLite के पास एक कुंजी है कि चाबियाँ कितनी बड़ी हो सकती हैं या यह वास्तव में कुछ ऊपरी सीमा पर मुख्य मूल्य को काटता है और शेष को डिस्क पर "अतिप्रवाह पृष्ठ" में रखता है। यह रैंडम i / o डबल्स के रूप में विशाल कुंजियों के लिए प्रश्नों को भयावह बना सकता है। यहां "B- ट्री पेज" अनुभाग तक स्क्रॉल करें sqlite.org/fileformat2.html
रियाद कल्ला

जवाबों:


7

आप अपने कुंजी डेटा वाले ब्लॉक में निश्चित-आकार के ऑफसेट की सूची के रूप में अपने सूचकांक को स्टोर कर सकते हैं। उदाहरण के लिए:

+--------------+
| 3            | number of entries
+--------------+
| 16           | offset of first key data
+--------------+
| 24           | offset of second key data
+--------------+
| 39           | offset of third key data
+--------------+
| key one |
+----------------+
| key number two |
+-----------------------+
| this is the third key |
+-----------------------+

(ठीक है, प्रमुख डेटा को वास्तविक उदाहरण में क्रमबद्ध किया जाएगा, लेकिन आपको यह विचार मिल जाएगा)।

ध्यान दें कि यह आवश्यक रूप से प्रतिबिंबित नहीं करता है कि वास्तव में किसी भी डेटाबेस में सूचकांक ब्लॉकों का निर्माण कैसे किया जाता है। यह केवल एक उदाहरण है कि आप सूचकांक डेटा के एक ब्लॉक को कैसे व्यवस्थित कर सकते हैं जहां कुंजी डेटा चर लंबाई का है।


ग्रेग, मैंने आपका जवाब अभी तक डिफैक्टो के उत्तर के रूप में नहीं लिया है क्योंकि मैं कुछ और प्रतिक्रिया के साथ-साथ अन्य DBMS में कुछ और शोध करने की उम्मीद कर रहा हूं (मैं मूल क्यू में अपनी टिप्पणी जोड़ रहा हूं)। अब तक सबसे आम दृष्टिकोण ऊपरी बाध्य टोपी लगता है और फिर एक अतिप्रवाह तालिका में बाकी की कुंजी जो केवल तभी जांची जाती है जब पूर्ण कुंजी की आवश्यकता होती है। नहीं कि सुरुचिपूर्ण। आपके समाधान में इसकी कुछ लालित्य है जो मुझे पसंद है, लेकिन किनारे के मामले में जहां चाबियाँ हमारे पृष्ठ आकार को उड़ाती हैं, आपके रास्ते को अभी भी एक अतिप्रवाह तालिका की आवश्यकता होगी या बस इसे अनुमति नहीं देना चाहिए।
रियाद कल्ला

मैं अंतरिक्ष से बाहर भाग गया ... संक्षेप में अगर डीबी डिजाइनर कुंजी आकार पर कुछ कठिन सीमाओं के साथ रह सकता है, तो मुझे लगता है कि आपका दृष्टिकोण सबसे कुशल और लचीला है। अंतरिक्ष और सीपीयू दक्षता का अच्छा कॉम्बो। ओवरफ्लो टेबल अधिक लचीली होती हैं, लेकिन लगातार ओवरफ्लो होने वाली कुंजियों के लिए रैंडम i / o को जोड़ने के लिए खौफनाक हो सकता है। इस पर इनपुट के लिए धन्यवाद!
रियाद कल्ला

ग्रेग, मैं इसके बारे में अधिक से अधिक सोच रहा हूं, वैकल्पिक समाधानों को देख रहा हूं और मुझे लगता है कि आपने इसे ऑफसेट हेडर विचार के साथ नामांकित किया है। यदि आप अपने ब्लॉकों को छोटा रखते हैं तो आप 8-बिट (1-बाइट) ऑफसेट के साथ दूर हो सकते हैं, बड़े ब्लॉक के साथ 16-बिट 128KB या 256KB ब्लॉक तक भी सबसे सुरक्षित होगा जो उचित होना चाहिए (4 या 8byx कुंजियों को ग्रहण करेगा)। बड़ी जीत यह है कि ऑफसेट डेटा में आप कितना सस्ता और तेज पढ़ सकते हैं और परिणामस्वरूप आप कितना डीसर्लाइजेशन बचा सकते हैं। उत्कृष्ट सुझाव, फिर से धन्यवाद।
रियाद कल्ला

यह अपस्केलबीडी में उपयोग किया जाने वाला दृष्टिकोण भी है: upscaledb.com/about.html#varlength
मैथ्यू रोडिक
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.