यह प्रश्न जेक मैन्सके द्वारा #sqlhelp पर पोस्ट किया गया था, और इसे एरिक डार्लिंग द्वारा मेरे ध्यान में लाया गया था।
मुझे याद नहीं है कि कभी भी sys.partitions
. के साथ कोई प्रदर्शन समस्या हुई हो . मेरा प्रारंभिक विचार (जॉय डी'एंटोनी द्वारा प्रतिध्वनित) यह था कि data_compression
पर एक फ़िल्टर कॉलम चाहिए अनावश्यक स्कैन से बचें, और क्वेरी रनटाइम को लगभग आधा कर दें। हालाँकि, यह विधेय नीचे धकेला नहीं जाता है, और यही कारण है कि थोड़ा सा अनपैकिंग करता है।
sys.partitions धीमा क्यों है?
यदि आप sys.partitions
. की परिभाषा देखें तो , यह मूल रूप से जेक का वर्णन है - एक UNION ALL
तीन . के साथ, सभी कॉलमस्टोर और रोस्टोर पार्टिशन में से sys.sysrowsets
. के स्पष्ट संदर्भ (संक्षिप्त स्रोत यहां):
CREATE VIEW sys.partitions AS WITH partitions_columnstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs.rowsetid, 0, 0, 0) ct -------- *** ^^^^^^^^^^^^^^ *** LEFT JOIN sys.syspalvalues cl ... WHERE ... sysconv(bit, rs.status & 0x00010000) = 1 -- Consider only columnstore base indexes ), partitions_rowstore(...cols...) AS ( SELECT ...cols..., cmprlevel AS data_compression ... FROM sys.sysrowsets rs -------- *** ^^^^^^^^^^^^^^ *** LEFT JOIN sys.syspalvalues cl ... WHERE ... sysconv(bit, rs.status & 0x00010000) = 0 -- Ignore columnstore base indexes and orphaned rows. ) SELECT ...cols... from partitions_rowstore p OUTER APPLY OpenRowset(TABLE ALUCOUNT, p.partition_id, 0, 0, p.object_id) ct union all SELECT ...cols... FROM partitions_columnstore as P1 LEFT JOIN (SELECT ...cols... FROM sys.sysrowsets rs OUTER APPLY OpenRowset(TABLE ALUCOUNT, rs.rowsetid, 0, 0, 0) ct ------- *** ^^^^^^^^^^^^^^ *** ) ...
यह दृश्य एक साथ उलझा हुआ लगता है, शायद पश्चगामी संगतता चिंताओं के कारण। यह निश्चित रूप से अधिक कुशल होने के लिए फिर से लिखा जा सकता है, विशेष रूप से केवल sys.sysrowsets
को संदर्भित करने के लिए और TABLE ALUCOUNT
एक बार वस्तुओं। लेकिन अभी आप या मैं इसके बारे में बहुत कुछ नहीं कर सकते।
कॉलम cmprlevel
sys.sysrowsets
. से आता है (स्तंभ संदर्भ पर एक उपनाम उपसर्ग मददगार होता)। आप उम्मीद करेंगे कि किसी OUTER APPLY
से पहले किसी कॉलम के सामने एक विधेय तार्किक रूप से घटित होगा। और एक स्कैन को रोक सकता है, लेकिन ऐसा नहीं होता है। निम्नलिखित सरल क्वेरी चलाना:
SELECT * FROM sys.partitions AS p INNER JOIN sys.objects AS o ON p.object_id = o.object_id WHERE o.is_ms_shipped = 0;
डेटाबेस में कॉलमस्टोर इंडेक्स होने पर निम्न योजना प्राप्त होती है (विस्तार करने के लिए क्लिक करें):
sys.partitions के लिए योजना, जिसमें कॉलमस्टोर इंडेक्स मौजूद हैं
और निम्न योजना जब नहीं हैं (विस्तार करने के लिए क्लिक करें):
sys.partitions के लिए योजना, जिसमें कोई कॉलमस्टोर इंडेक्स मौजूद नहीं है
ये वही अनुमानित योजना हैं, लेकिन जब किसी ऑपरेशन को रनटाइम पर छोड़ दिया जाता है तो सेंट्रीऑन प्लान एक्सप्लोरर हाइलाइट करने में सक्षम होता है। यह बाद के मामले में तीसरे स्कैन के लिए होता है, लेकिन मुझे नहीं पता कि रनटाइम स्कैन गिनती को और कम करने का कोई तरीका है; दूसरा स्कैन तब भी होता है जब क्वेरी शून्य पंक्तियाँ लौटाती है।
जेक के मामले में, उसके पास बहुत कुछ . है वस्तुओं की, इसलिए इस स्कैन को दो बार भी करना ध्यान देने योग्य, दर्दनाक और एक बार बहुत अधिक है। और ईमानदारी से मुझे नहीं पता कि TABLE ALUCOUNT
, एक आंतरिक और गैर-दस्तावेज लूपबैक कॉल, को इनमें से कुछ बड़ी वस्तुओं को कई बार स्कैन करना पड़ता है।
स्रोत पर पीछे मुड़कर देखने पर, मुझे आश्चर्य हुआ कि क्या कोई अन्य विधेय है जो उस दृष्टिकोण को पारित किया जा सकता है जो योजना के आकार को मजबूर कर सकता है, लेकिन मुझे नहीं लगता कि ऐसा कुछ भी है जिसका प्रभाव हो सकता है।
क्या कोई अन्य दृश्य काम करेगा?
हालाँकि, हम पूरी तरह से एक अलग दृष्टिकोण की कोशिश कर सकते हैं। मैंने अन्य विचारों की तलाश की जिनमें sys.sysrowsets
. दोनों के संदर्भ शामिल थे और ALUCOUNT
, और कई हैं जो सूची में दिखाई देते हैं, लेकिन केवल दो आशाजनक हैं:sys.internal_partitions
और sys.system_internals_partitions
.
sys.internal_partitions
मैंने कोशिश की sys.internal_partitions
पहला:
SELECT * FROM sys.internal_partitions AS p INNER JOIN sys.objects AS o ON p.object_id = o.object_id WHERE o.is_ms_shipped = 0;
लेकिन योजना ज्यादा बेहतर नहीं थी (विस्तार करने के लिए क्लिक करें):
sys.internal_partitions के लिए योजना
sys.sysrowsets
. के विरुद्ध केवल दो स्कैन हैं इस बार, लेकिन स्कैन वैसे भी अप्रासंगिक हैं क्योंकि क्वेरी उन पंक्तियों को बनाने के करीब नहीं आती है जिनमें हम रुचि रखते हैं। हम केवल कॉलमस्टोर से संबंधित वस्तुओं के लिए पंक्तियाँ देखते हैं (जैसा कि दस्तावेज़ीकरण बताता है)।
sys.system_internals_partitions
आइए कोशिश करते हैं sys.system_internals_partitions
. मैं इसके बारे में थोड़ा सावधान हूं, क्योंकि यह असमर्थित है (यहां चेतावनी देखें), लेकिन एक क्षण मेरे साथ रहें:
SELECT * FROM sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id = o.object_id WHERE o.is_ms_shipped = 0;
कॉलमस्टोर इंडेक्स वाले डेटाबेस में, sys.sysschobjs
के खिलाफ एक स्कैन होता है , लेकिन अब केवल एक sys.sysrowsets
. के विरुद्ध स्कैन करें (विस्तार करने के लिए क्लिक करें):
sys.system_internals_partitions के लिए योजना, जिसमें कॉलमस्टोर इंडेक्स मौजूद हैं
यदि हम डेटाबेस में एक ही क्वेरी को बिना किसी कॉलमस्टोर इंडेक्स के चलाते हैं, तो योजना और भी सरल है, जिसमें sys.sysschobjs
की तलाश है। (विस्तार करने के लिए क्लिक करें):
sys.system_internals_partitions के लिए योजना, जिसमें कोई कॉलमस्टोर इंडेक्स मौजूद नहीं है
हालांकि, यह काफी नहीं है हम क्या कर रहे हैं, या कम से कम जेक के बाद क्या नहीं था, क्योंकि इसमें कॉलमस्टोर इंडेक्स से कलाकृतियों को भी शामिल किया गया है। यदि हम इन फ़िल्टरों को जोड़ते हैं, तो वास्तविक आउटपुट अब हमारी पहले की, अधिक महंगी क्वेरी से मेल खाता है:
SELECT * FROM sys.system_internals_partitions AS p INNER JOIN sys.objects AS o ON p.object_id = o.object_id WHERE o.is_ms_shipped = 0 AND p.is_columnstore = 0 AND p.is_orphaned = 0;
एक बोनस के रूप में, sys.sysschobjs
. के विरुद्ध स्कैन करें कॉलमस्टोर ऑब्जेक्ट्स वाले डेटाबेस में भी एक तलाश बन गई है। हममें से अधिकांश लोग उस अंतर पर ध्यान नहीं देंगे, लेकिन यदि आप जेक जैसे परिदृश्य में हैं, तो आप शायद (विस्तार करने के लिए क्लिक करें):
sys.system_internals_partitions के लिए अतिरिक्त फ़िल्टर के साथ सरल योजना
sys.system_internals_partitions
sys.partitions
. की तुलना में स्तंभों के भिन्न सेट को प्रदर्शित करता है (कुछ पूरी तरह से अलग हैं, अन्य के नए नाम हैं) इसलिए, यदि आप डाउनस्ट्रीम आउटपुट का उपभोग कर रहे हैं, तो आपको उनके लिए समायोजित करना होगा। आप यह भी सत्यापित करना चाहेंगे कि यह रोस्टोर, मेमोरी-ऑप्टिमाइज़्ड और कॉलमस्टोर इंडेक्स में आपकी इच्छित सभी जानकारी लौटाता है, और उन अजीब ढेर के बारे में मत भूलना। और, अंत में, s
. को छोड़ने के लिए तैयार रहें internals
. में कई, कई बार।
निष्कर्ष
जैसा कि मैंने ऊपर उल्लेख किया है, यह सिस्टम दृश्य आधिकारिक रूप से समर्थित नहीं है, इसलिए इसकी कार्यक्षमता किसी भी समय बदल सकती है; इसे डेडिकेटेड एडमिनिस्ट्रेटर कनेक्शन (DAC) के तहत भी स्थानांतरित किया जा सकता है, या उत्पाद से पूरी तरह से हटाया जा सकता है। इस दृष्टिकोण का उपयोग करने के लिए स्वतंत्र महसूस करें यदि sys.partitions
आपके लिए अच्छा काम नहीं कर रहा है, लेकिन कृपया सुनिश्चित करें कि आपके पास एक बैकअप योजना है। और सुनिश्चित करें कि जब आप SQL सर्वर के भविष्य के संस्करणों का परीक्षण शुरू करते हैं, या अपग्रेड करने के बाद, बस मामले में इसे आपके द्वारा प्रतिगमन परीक्षण के रूप में प्रलेखित किया जाता है।