संस्करण 3.2 से शुरू होकर, MongoDB हर इंडेक्स के लिए उपयोग के आंकड़ों को ट्रैक करता है। इन आंकड़ों तक पहुंचने के लिए, MongoDB $indexStats एग्रीगेशन पाइपलाइन चरण प्रदान करता है। यहां MongoDB में अनयूज्ड इंडेक्स खोजने पर छह विचार दिए गए हैं।
उदाहरण के लिए, निम्न कमांड "test.foo" कलेक्शन के लिए इंडेक्स आंकड़े प्रदान करेगा:
db.foo.aggregate( [ { $indexStats: { } } ] )
हम $indexStats आउटपुट का वर्णन नहीं करेंगे क्योंकि इस विषय को कवर करने के लिए बहुत सारे दस्तावेज और कई अच्छे लेख मौजूद हैं। इसके बजाय, हम $indexStats ऑपरेटर का उपयोग करते समय विभिन्न क्षेत्रों पर छह विचार प्रदान करेंगे।
विचार 1: हर सर्विस रीस्टार्ट पर आंकड़े रीसेट हो जाते हैं
$indexStats ऑपरेटर का उपयोग करते समय हमेशा "accesses.since" फील्ड पर अतिरिक्त ध्यान दें। कुछ क्वेरी पैटर्न दुर्लभ हो सकते हैं, जैसे दिन के अंत का बैच प्रोसेस या साप्ताहिक रिपोर्ट, इसलिए सुनिश्चित करें कि जिन आंकड़ों का आप मूल्यांकन करने जा रहे हैं, उनकी अवधि आपकी आवश्यकताओं को पूरा करती है। निम्न स्क्रिप्ट आपको एक थ्रेशोल्ड (घंटों में) सेट करने की अनुमति देती है और यदि कोई अनयूज्ड इंडेक्स इसका अनुपालन नहीं करता है तो चेतावनी प्रिंट करेगा।
threshold_hours=24; db.foo.aggregate( [ { $indexStats: { } } ] ).forEach(function(f){if (f.accesses.ops==0) {if (ISODate()-f.accesses.since < threshold_hours*3600*1000) {print('Index: ' +f.name+ ' accessed: ' +f.accesses.ops+ ' times, Status:WARNING, The duration of statistics DOES NOT meet your compliance')} else {print('Index: ' +f.name+ ' accessed: ' +f.accesses.ops+ ' times, Status:OK The duration of statistics meet your compliance');};}})
आपके लिए सही थ्रेशोल्ड क्या है? इसका कोई सरल उत्तर नहीं है। सभी स्टेटमेंट पैटर्न समान आवृत्ति के साथ निष्पादित नहीं होते हैं, इसलिए सही थ्रेशोल्ड एप्लिकेशन से एप्लिकेशन में भिन्न होता है और एक ही डेटाबेस के भीतर प्रति कलेक्शन भी अलग हो सकता है।
विचार 2: सेकेंडरी रीड्स
डिफ़ॉल्ट रूप से, $indexStats प्राइमरी से पढ़ता है। यदि आपका एप्लिकेशन केवल सेकेंडरी से पढ़ता है, तो $indexStats को प्राइमरी के खिलाफ चलाने से गलत निष्कर्ष निकलेंगे। सेकेंडरी से परिणाम पढ़ने के लिए, आप पहले विचार से ऊपर की स्क्रिप्ट का उपयोग कर सकते हैं, लेकिन इसे चलाने से पहले db.getMongo().setReadPref('secondary') निष्पादित करें, जो कमांड को सेकेंडरी से पढ़ने के लिए मजबूर करता है।
अब सवाल यह है कि "क्या होगा यदि एक कलेक्शन को प्राइमरी और सेकेंडरी दोनों रीड्स प्राप्त होते हैं?" निम्न स्क्रिप्ट का उपयोग करके, आप idx सरणी को उन इंडेक्स के साथ भर सकते हैं जो प्राइमरी और सेकेंडरी दोनों पर अनयूज्ड हैं:
idx=[]; db.foo.getIndexes().forEach(function(f){idx.push(f.name)}) db.getMongo().setReadPref('primary'); db.foo.aggregate( [ { $indexStats: { } } ] ).forEach(function(f){if (f.accesses.ops>0) { var index = idx.indexOf(f.name); if (index > -1) {idx.splice(index, 1);};}}) db.getMongo().setReadPref('secondary'); db.foo.aggregate( [ { $indexStats: { } } ] ).forEach(function(f){if (f.accesses.ops>0) { var index = idx.indexOf(f.name); if (index > -1) {idx.splice(index, 1);};}})
idx सरणी की सामग्री में प्राइमरी और सेकेंडरी दोनों पर अनयूज्ड इंडेक्स होंगे। अनुपालन विंडो (threshold_hours) के बारे में क्या? विचार 1 (पिछले उपखंड) से स्क्रिप्ट को थोड़ा संशोधित करके हम समय अनुपालन लागू कर सकते हैं। बस एग्रीगेशन भाग को नीचे दिए गए के साथ बदलें और setReadPref का उपयोग करके प्राइमरी और सेकेंडरी के खिलाफ स्क्रिप्ट निष्पादित करें:
db.foo.aggregate( [ { $indexStats: { } } ,{$match:{"name" : {$in:idx}}}] ).
यदि प्राइमरी और सेकेंडरी अलग-अलग अनुपालन परिणाम लौटाते हैं, तो सबसे अच्छा तरीका है कि दोनों टियर्स के अनुपालन होने तक प्रतीक्षा करें।
सेकेंडरी रीड प्रेफरेंस का उपयोग "गलत परिणामों" का कारण बन सकता है यदि कोई सेकेंडरी हाल ही में रीस्टार्ट हुआ है और स्क्रिप्ट उस सेकेंडरी को आंकड़े खींचने के लिए चुनती है। इस मामले के लिए, एक अच्छा तरीका है db.serverStatus().uptime की जांच करना और उच्च अपटाइम वाले सेकेंडरी को चुनना। अपटाइम विधि के लिए स्क्रिप्ट पर एक अलग दृष्टिकोण की आवश्यकता होती है जिसे इस ब्लॉग पोस्ट के भविष्य के संशोधन में लागू किया जाएगा।
विचार 3: रेप्लिका सेट टैग्स
रेप्लिका सेट टैग्स कई उपयोग के मामलों में लागू होते हैं लेकिन मुख्य रूप से जियो-रेप्लिकेशन परिदृश्य में रीड इलाके के लिए और विशिष्ट वर्कलोड को समर्पित नोड्स पर लक्षित करने के लिए उपयोग किए जाते हैं (उदाहरण के लिए, भारी एनालिटिक्स ऑपरेशन)। जब वर्कलोड टार्गेटिंग की बात आती है, तो टैग किए गए नोड्स की अलग से जांच की जानी चाहिए क्योंकि वे उन इंडेक्स का उपयोग कर सकते हैं जिनका अन्य नोड्स उपयोग नहीं करते। यह जियो-रेप्लिकेशन में भी हो सकता है, हालांकि यह अधिक दुर्लभ है। विचार 1 की स्क्रिप्ट का उपयोग टैग किए गए सदस्य की जांच के लिए किया जा सकता है, जिसमें स्क्रिप्ट की शुरुआत में रीड प्रेफरेंस जोड़ा जाता है। यहां एक उदाहरण है जो रीड प्रेफरेंस को एनालिटिक्स के लिए चिह्नित सेकेंडरी पर सेट करता है:
db.getMongo().setReadPref('secondary', [ { "workload": "analytics" } ] ) .
दूसरे विचार के अंतिम पैराग्राफ में वर्णित भविष्य का दृष्टिकोण, टैग किए गए सेकेंडरी की जांच के लिए भी विस्तारित किया जा सकता है।
विचार 4: TTL इंडेक्स
TTL इंडेक्स ऑपरेशंस की सेवा करते हैं लेकिन उनका मुख्य उपयोग डेटा प्रूनिंग के लिए होता है। $indexStats के लिए इन इंडेक्स को अनयूज्ड के रूप में रिपोर्ट करना बहुत संभावना है क्योंकि TTL मॉनिटर इंडेक्स ऑपरेशन के रूप में नहीं गिना जाता है। आप निश्चित रूप से $indexStats रिपोर्ट निष्कर्षों के आधार पर TTL इंडेक्स को ड्रॉप नहीं करना चाहेंगे इसलिए स्क्रिप्ट्स को इस प्रकार के इंडेक्स को बाहर करना होगा।
दूसरे खंड से idx सरणी को भरने वाली स्क्रिप्ट पर एक छोटा सा संशोधन काम करेगा। दूसरी लाइन को निम्नलिखित से बदलें और idx सरणी में कोई TTL इंडेक्स नहीं होगा।
db.foo.getIndexes().forEach(function(f){if (f.expireAfterSeconds==undefined) {idx.push(f.name)}})
बहिष्कृत इंडेक्स के बारे में चर्चा को एक कदम आगे ले जाते हुए, हम दावा कर सकते हैं कि _id भी इस श्रेणी में आता है। जाहिर है, आप _id इंडेक्स को ड्रॉप नहीं कर सकते भले ही यह अनयूज्ड हो। एक कलेक्शन जो कभी _id इंडेक्स को नहीं छूता, उसे रीडिजाइन की आवश्यकता हो सकती है।
विचार 5: शार्डेड क्लस्टर्स
जब शार्डेड क्लस्टर्स की बात आती है, तो $indexStats के आउटपुट का मूल्यांकन करने से पहले दो चीजों पर विचार करना होता है। पहला, शार्डेड कलेक्शन के खिलाफ $indexStats का उपयोग करते समय, यह संभव है कि शार्ड की इंडेक्स को अनयूज्ड के रूप में वर्गीकृत किया जाए। उदाहरण के लिए, एक राइट-हैवी कलेक्शन जो {_id:"hashed"} पर शार्डेड है (राइट्स के समान वितरण के लिए) लेकिन बिना किसी रीड/अपडेट/डिलीट ऑपरेशन के जो _id इंडेक्स का उपयोग कर सकते हैं। इस मामले में, {_id:"hashed"} अनयूज्ड के रूप में रिपोर्ट करता है। इंडेक्स को ड्रॉप करना अच्छा विचार नहीं है क्योंकि यह आपके शार्डेड क्लस्टर को तोड़ देगा। यदि आप 'stats.foo' कलेक्शन के लिए शार्ड की को बाहर करना चाहते हैं, तो निम्न स्क्रिप्ट को विचार दो (सेकेंडरी रीड्स) में वर्णित विधि से जोड़ा जा सकता है और शार्ड की को idx सरणी से बाहर रखा जाएगा।
shardkey=db.getSiblingDB('config').collections.findOne({_id:'stats.foo'},{_id:0,key:1}); db.foo.getIndexes().forEach(function(f){if (JSON.stringify(f.key)!=JSON.stringify(shardkey.key)) {printjson(shardkey.key);printjson(f.key);idx.push(f.name)}})
एक और विचार $indexStats के आउटपुट से संबंधित है, क्योंकि यह अब उन सभी शार्ड्स से आंकड़े लौटाता है जिनमें कलेक्शन की उपस्थिति है। हालांकि यह असामान्य है, ऐसे मामले हैं जहां एक इंडेक्स कुछ शार्ड्स में अनयूज्ड के रूप में रिपोर्ट करता है जबकि अन्य शार्ड्स इसका उपयोग कर रहे हैं। एक खराब शार्ड की कारण हो सकता है लेकिन सबसे आम परिदृश्य "कवरिंग इंडेक्स" नामक है।
कवरिंग इंडेक्स को बेहतर ढंग से समझने के लिए एक उदाहरण: इंडेक्स {a:1} और {a:1,b:1} दोनों फील्ड 'a' पर इक्वलिटी मैच की सेवा कर सकते हैं। यदि शार्ड A का ऑप्टिमाइज़र {a:1} चुनता है और शार्ड B का {a:1,b:1} चुनता है, तो दोनों इंडेक्स कम से कम एक शार्ड के लिए अनयूज्ड रिपोर्ट करेंगे।
चुनौती यह है कि कौन से इंडेक्स विश्व स्तर पर उपयोग में नहीं हैं। विचार 2 स्क्रिप्ट (सेकेंडरी रीड्स) के एग्रीगेशन भाग (forEach लूप के बाद) को निम्नलिखित से बदलने से काम हो जाएगा:
db.foo.aggregate( [ { $indexStats: { } } , {$group: {_id:"$name",number : {$sum:"$accesses.ops"}}}] ).forEach(function(f){if (f.number>0) { var index = idx.indexOf(f._id); if (index > -1) {idx.splice(index, 1);};}})
एक और चुनौती उन इंडेक्स की खोज करना है जो आंशिक रूप से उपयोग में हैं। यह जानकारी रिडंडेंट इंडेक्स या इंडेक्स पहचान खराबी के मामले में सहायक हो सकती है। idx सरणी का उपयोग करके, निम्न एग्रीगेशन/स्क्रिप्ट विश्व स्तर पर अनयूज्ड इंडेक्स के साथ भरी जाती है और आंशिक रूप से उपयोग में आने वाले इंडेक्स की रिपोर्ट करती है:
db.foo.aggregate( [ { $indexStats: { } },{$match:{name:{$nin:idx},"accesses.ops":0}}]).forEach(function(f){print("Index "+f.name+" reports as partially unused on shard/host " +f.host)})
विचार 6: लीस्ट-यूज्ड इंडेक्स
अनयूज्ड इंडेक्स की खोज करना और उन्हें ड्रॉप करना महत्वपूर्ण है लेकिन लीस्ट यूज्ड इंडेक्स का मूल्यांकन करना भी महत्वपूर्ण है। यदि कोई इंडेक्स केवल एक सप्ताह या महीनों में एक या दो बार एक्सेस किया जाता है, तो इसका मतलब यह हो सकता है कि यह आपके वर्कलोड के लिए आवश्यक या फायदेमंद नहीं है। निम्न एग्रीगेशन का उपयोग करके समय-समय पर लीस्ट-यूज्ड इंडेक्स की जांच करना हमेशा एक अच्छी प्रथा है:
db.foo.aggregate( [ { $indexStats: { } },{$match:{"accesses.ops":{$gt:0}}},{$group: {_id:"$name",number : {$sum:"$accesses.ops"}}},{$sort:{number:1}}] )
फिर, उचित कार्रवाई करें जो इंडेक्स को ड्रॉप करना, इंडेक्स परिभाषा बदलना, या यहां तक कि इंडेक्स को रिडंडेंट बनाने के लिए अपने स्कीमा/एप्लिकेशन लॉजिक को बदलना हो सकता है।
हम यहां आपकी मदद के लिए हैं
हम आपके MongoDB इंस्टेंस के लिए आपके इंडेक्स को साफ करने में आपकी मदद के लिए यहां हैं। आपको बस support@objectrocket.com पर एक टिकट बनाना है और हमें अपने इंडेक्स को बेहतर बनाने में आपकी मदद करने दें!