मैं अक्सर देखता हूं कि लोग SQL सर्वर के साथ संघर्ष करते हैं जब वे दो अलग-अलग निष्पादन योजनाओं को देख रहे होते हैं जो वे मानते हैं कि एक ही प्रश्न है। आमतौर पर यह अन्य अवलोकनों के बाद खोजा जाता है, जैसे कि बहुत अलग निष्पादन समय। मैं कहता हूं कि वे मानते हैं कि यह वही प्रश्न है, क्योंकि कभी-कभी ऐसा होता है, और कभी-कभी ऐसा नहीं होता है।
सबसे आम मामलों में से एक तब होता है जब वे एसएसएमएस में एक प्रश्न का परीक्षण कर रहे होते हैं और एक अलग योजना प्राप्त करते हैं जो उन्हें उनके आवेदन से मिलती है। यहां खेलने पर संभावित रूप से दो कारक हैं (जो तब भी प्रासंगिक हो सकते हैं जब तुलना एप्लिकेशन और एसएसएमएस के बीच न हो):
- एप्लिकेशन में लगभग हमेशा अलग होता है
SET
SSMS की तुलना में सेटिंग (येARITHABORT
. जैसी चीज़ें हैं) ,ANSI_NULLS
औरQUOTED_IDENTIFIER
) यह SQL सर्वर को दो योजनाओं को अलग-अलग संग्रहीत करने के लिए बाध्य करता है; एरलैंड सोमरस्कोग ने अपने लेख, स्लो इन द एप्लीकेशन, फास्ट इन एसएसएमएस?
- जब योजना की प्रतिलिपि पहली बार संकलित की गई थी, तब अनुप्रयोग द्वारा उपयोग किए जाने वाले पैरामीटर बहुत भिन्न हो सकते थे, और एसएसएमएस से पहली बार क्वेरी चलाने के लिए उपयोग किए जाने वाले लोगों की तुलना में एक अलग योजना का नेतृत्व कर सकते थे - इसे पैरामीटर स्नीफिंग के रूप में जाना जाता है . एरलैंड इसके बारे में भी गहराई से बात करता है, और मैं उसकी सिफारिशों को दोबारा नहीं दोहराऊंगा, लेकिन आपको याद दिलाते हुए संक्षेप में बताऊंगा कि एसएसएमएस में एप्लिकेशन की क्वेरी का परीक्षण करना हमेशा उपयोगी नहीं होता है, क्योंकि यह एक सेब-से-सेब परीक्षण होने की संभावना नहीं है।ली>
कुछ अन्य परिदृश्य हैं जो थोड़े अधिक अस्पष्ट हैं जिन्हें मैं अपनी बुरी आदतों और सर्वोत्तम प्रथाओं के बारे में बताता हूँ। ये ऐसे मामले हैं जहां योजनाएं अलग नहीं हैं, लेकिन एक ही योजना की कई प्रतियां हैं जो योजना कैश को फुलाती हैं। मैंने सोचा कि मुझे उनका यहाँ उल्लेख करना चाहिए क्योंकि वे हमेशा इतने सारे लोगों को आश्चर्यचकित करते हैं।
cAsE और व्हाइटस्पेस महत्वपूर्ण हैं
SQL सर्वर क्वेरी टेक्स्ट को बाइनरी प्रारूप में हैश करता है, जिसका अर्थ है कि क्वेरी टेक्स्ट में प्रत्येक वर्ण महत्वपूर्ण है। आइए निम्नलिखित सरल प्रश्नों को लें:
USE AdventureWorks2014; DBCC FREEPROCCACHE WITH NO_INFOMSGS; GO SELECT StoreID FROM Sales.Customer; GO -- original query GO SELECT StoreID FROM Sales.Customer; GO ----^---- extra space GO SELECT storeid FROM sales.customer; GO ---- lower case names GO select StoreID from Sales.Customer; GO ---- lower case keywords GO
ये स्पष्ट रूप से समान परिणाम उत्पन्न करते हैं, और ठीक उसी योजना को उत्पन्न करते हैं। हालांकि, अगर हम देखें कि हमारे पास प्लान कैश में क्या है:
SELECT t.[text], p.size_in_bytes, p.usecounts FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t WHERE LOWER(t.[text]) LIKE N'%sales'+'.'+'customer%';
परिणाम दुर्भाग्यपूर्ण हैं:
तो, इस मामले में, यह स्पष्ट है कि केस और व्हाइटस्पेस बहुत महत्वपूर्ण हैं। मैंने पिछले मई में इस बारे में और अधिक विस्तार से बात की थी।
स्कीमा संदर्भ महत्वपूर्ण हैं
मैंने पहले किसी ऑब्जेक्ट को संदर्भित करते समय स्कीमा उपसर्ग को निर्दिष्ट करने के महत्व के बारे में ब्लॉग किया है, लेकिन उस समय मुझे पूरी तरह से पता नहीं था कि इसमें योजना कैश निहितार्थ भी थे।
आइए एक बहुत ही सरल मामले पर एक नज़र डालें जहां हमारे पास अलग-अलग डिफ़ॉल्ट स्कीमा वाले दो उपयोगकर्ता हैं, और वे एक ही क्वेरी टेक्स्ट चलाते हैं, ऑब्जेक्ट को इसके स्कीमा द्वारा संदर्भित करने में विफल होते हैं:
USE AdventureWorks2014; DBCC FREEPROCCACHE WITH NO_INFOMSGS; GO CREATE USER SQLPerf1 WITHOUT LOGIN WITH DEFAULT_SCHEMA = Sales; CREATE USER SQLPerf2 WITHOUT LOGIN WITH DEFAULT_SCHEMA = Person; GO CREATE TABLE dbo.AnErrorLog(id INT); GRANT SELECT ON dbo.AnErrorLog TO SQLPerf1, SQLPerf2; GO EXECUTE AS USER = N'SQLPerf1'; GO SELECT id FROM AnErrorLog; GO REVERT; GO EXECUTE AS USER = N'SQLPerf2'; GO SELECT id FROM AnErrorLog; GO REVERT; GO
अब, यदि हम प्लान कैश पर एक नज़र डालें, तो हम sys.dm_exec_plan_attributes
में खींच सकते हैं यह देखने के लिए कि हमें समान प्रश्नों के लिए दो अलग-अलग योजनाएं क्यों मिल रही हैं:
SELECT t.[text], p.size_in_bytes, p.usecounts, [schema_id] = pa.value, [schema] = s.name FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t CROSS APPLY sys.dm_exec_plan_attributes(p.plan_handle) AS pa INNER JOIN sys.schemas AS s ON s.[schema_id] = pa.value WHERE t.[text] LIKE N'%AnError'+'Log%' AND pa.attribute = N'user_id';
परिणाम:
और अगर आप इसे फिर से चलाते हैं लेकिन dbo.
. जोड़ें दोनों प्रश्नों के उपसर्ग, आप देखेंगे कि केवल एक योजना है जो दो बार उपयोग की जाती है। हमेशा पूरी तरह से संदर्भित वस्तुओं के लिए यह एक बहुत ही सम्मोहक तर्क बन जाता है।
सेटिंग रिडक्स सेट करें
एक साइड नोट के रूप में, आप यह निर्धारित करने के लिए एक समान दृष्टिकोण का उपयोग कर सकते हैं कि क्या SET
एक ही क्वेरी के दो या अधिक संस्करणों के लिए सेटिंग्स भिन्न होती हैं। इस मामले में हम एक ही संग्रहित प्रक्रिया के लिए विभिन्न कॉलों द्वारा उत्पन्न कई योजनाओं से जुड़े प्रश्नों की जांच कर रहे हैं, लेकिन आप उन्हें क्वेरी टेक्स्ट या क्वेरी हैश द्वारा भी पहचान सकते हैं।
SELECT p.plan_handle, p.usecounts, p.size_in_bytes, set_options = MAX(a.value) FROM sys.dm_exec_cached_plans AS p CROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS t CROSS APPLY sys.dm_exec_plan_attributes(p.plan_handle) AS a WHERE t.objectid = OBJECT_ID(N'dbo.procedure_name') AND a.attribute = N'set_options' GROUP BY p.plan_handle, p.usecounts, p.size_in_bytes;
यदि आपके यहां एक से अधिक परिणाम हैं तो आपको set_options
. के लिए अलग-अलग मान दिखाई देने चाहिए (जो एक बिटमास्क है)। अभी तो शुरुआत है; मैं यहां से निपटने जा रहा हूं और आपको बताता हूं कि आप यहां "मूल्यांकन सेट विकल्प" अनुभाग के अनुसार मूल्य को अनपैक करके यह निर्धारित कर सकते हैं कि प्रत्येक योजना के लिए कौन से विकल्प सक्षम हैं। हाँ, मैं बहुत आलसी हूँ।
निष्कर्ष
एक ही क्वेरी के लिए आपको अलग-अलग योजनाएँ दिखाई देने के कई कारण हो सकते हैं (या जो आपको लगता है वह एक ही क्वेरी है)। ज्यादातर मामलों में आप कारण को बहुत आसानी से अलग कर सकते हैं; चुनौती अक्सर इसे पहले स्थान पर देखना जान रही है। मेरी अगली पोस्ट में, मैं थोड़ा अलग विषय के बारे में बात करूंगा:एक "समान" सर्वर पर पुनर्स्थापित डेटाबेस एक ही क्वेरी के लिए अलग-अलग योजनाएं क्यों प्राप्त कर सकता है।