SQL सर्वर 2008 डेटाइम इंडेक्स प्रदर्शन बग


11

हम SQL Server 2008 R2 का उपयोग कर रहे हैं, और एक प्राथमिक आईडी इंडेक्स के साथ एक बहुत बड़ी (100M + पंक्तियाँ) तालिका और datetimeएक गैर-अनुक्रमित सूचकांक वाला एक कॉलम है। हम कुछ अत्यधिक असामान्य क्लाइंट / सर्वर व्यवहार को विशेष रूप से अनुक्रमित डेटाइम कॉलम पर एक order byक्लॉज के उपयोग के आधार पर देख रहे हैं ।

मैं निम्नलिखित पोस्ट के माध्यम से पढ़ता हूं: /programming/1716798/sql-server-2008-ordering-by-datetime-is-too-slow लेकिन क्लाइंट या सर्वर के साथ जो चल रहा है उससे अधिक चल रहा है यहाँ वर्णित करें।

यदि हम निम्नलिखित क्वेरी चलाते हैं (कुछ सामग्री की सुरक्षा के लिए संपादित):

select * 
from [big table] 
where serial_number = [some number] 
order by test_date desc

हर बार क्वेरी आउट-आउट। SQL Server Profiler में निष्पादित क्वेरी सर्वर के लिए इस तरह दिखता है:

exec sp_cursorprepexec @p1 output,@p2 output,NULL,N'select * .....

अब यदि आप क्वेरी को संशोधित करते हैं, तो यह कहें:

declare @temp int;
select * from [big table] 
where serial_number = [some number] 
order by test_date desc

SQL सर्वर प्रोफाइलर निष्पादित क्वेरी को सर्वर के समान दिखता है, और यह तुरंत काम करता है:

exec sp_prepexec @p1 output, NULL, N'declare @temp int;select * from .....

तथ्य की बात के रूप में, आप एक अप्रयुक्त घोषित बयान के बजाय एक खाली टिप्पणी ('-' ') भी डाल सकते हैं और समान परिणाम प्राप्त कर सकते हैं। इसलिए शुरू में हम इस मुद्दे के मूल कारण के रूप में सपा पूर्व प्रोसेसर की ओर इशारा कर रहे थे, लेकिन अगर आप ऐसा करते हैं:

select * 
from [big table] 
where serial_number = [some number] 
order by Cast(test_date as smalldatetime) desc

यह तुरन्त भी काम करता है (आप इसे किसी अन्य datetimeप्रकार के रूप में भी डाल सकते हैं ), मिलीसेकंड में परिणाम लौटाता है। और प्रोफाइलर सर्वर के अनुरोध को निम्नानुसार दिखाता है:

exec sp_cursorprepexec @p1 output, @p2 output, NULL, N'select * from .....

ताकि sp_cursorprepexecमुद्दे के पूर्ण कारण से प्रक्रिया को कुछ हद तक बाहर रखा जा सके । इसे इस तथ्य में जोड़ें कि इसे sp_cursorprepexec'ऑर्डर द्वारा' का उपयोग नहीं किए जाने पर भी कॉल किया जाता है और इसका परिणाम भी तुरंत मिलता है।

हम इस मुद्दे के लिए काफी घूमा करते हैं, और मैं दूसरों से मिलते-जुलते मुद्दों को देखता हूं, लेकिन कोई भी इसे इस स्तर तक नहीं ले जाता है।

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

संपादन:

मुझे एक डाल भी डालनी चाहिए जिससे forceseekसमस्या गायब हो जाए।

मुझे खोजकर्ताओं की मदद करने के लिए जोड़ना चाहिए, ODBC टाइमआउट त्रुटि को फेंक दिया गया है: [Microsoft] [ODBC SQL सर्वर ड्राइवर] ऑपरेशन रद्द

10/12/2012 को जोड़ा गया: फिर भी मूल कारण का शिकार करना, (Microsoft को देने के लिए एक नमूना बनाने के साथ, मैं प्रस्तुत करने के बाद यहां किसी भी परिणाम को पार करूंगा)। मैं कार्यशील क्वेरी (अतिरिक्त टिप्पणी / घोषणा विवरण के साथ) और गैर-कार्यशील क्वेरी के बीच ODBC ट्रेस फ़ाइल में खुदाई कर रहा हूं। मूलभूत ट्रेस अंतर नीचे पोस्ट किया गया है। यह SQLBindCol चर्चाओं के पूरा होने के बाद SQLExtendedFetch कॉल करने के लिए कॉल पर होता है। कॉल रिटर्न कोड -1 के साथ विफल रहता है, और पैरेंट थ्रेड SQLCancel में प्रवेश करता है। चूंकि हम मूल ग्राहक और विरासत ODBC ड्राइवरों दोनों के साथ इसका उत्पादन करने में सक्षम हैं, इसलिए मैं अभी भी सर्वर साइड पर कुछ संगतता समस्या की ओर इशारा कर रहा हूं।

(clip)
MSSQLODBCTester 1664-1718   EXIT  SQLBindCol  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10
        UWORD                       16 
        SWORD                        1 <SQL_C_CHAR>
        PTR                0x03259030
        SQLLEN                    51
        SQLLEN *            0x0326B820 (0)

MSSQLODBCTester 1664-1718   ENTER SQLExtendedFetch 
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

MSSQLODBCTester 1664-1fd0   ENTER SQLCancel 
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   EXIT  SQLExtendedFetch  with return code -1 (SQL_ERROR)
        HSTMT               0x001EEA10
        UWORD                        1 <SQL_FETCH_NEXT>
        SQLLEN                     1
        SQLULEN *           0x032677C4
        UWORD *             0x032679B0

        DIAG [S1008] [Microsoft][ODBC SQL Server Driver]Operation canceled (0) 

MSSQLODBCTester 1664-1fd0   EXIT  SQLCancel  with return code 0 (SQL_SUCCESS)
        HSTMT               0x001EEA10

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 0 (SQL_SUCCESS)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C [       5] "S1008"
        SDWORD *            0x08BFFF08 (0)
        WCHAR *             0x08BFF85C [      53] "[Microsoft][ODBC SQL Server Driver]Operation canceled"
        SWORD                      511 
        SWORD *             0x08BFFEE6 (53)

MSSQLODBCTester 1664-1718   ENTER SQLErrorW 
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6

MSSQLODBCTester 1664-1718   EXIT  SQLErrorW  with return code 100 (SQL_NO_DATA_FOUND)
        HENV                0x001E7238
        HDBC                0x001E7B30
        HSTMT               0x001EEA10
        WCHAR *             0x08BFFC5C
        SDWORD *            0x08BFFF08
        WCHAR *             0x08BFF85C 
        SWORD                      511 
        SWORD *             0x08BFFEE6
(clip)

Microsoft कनेक्ट केस 10/12/2012 जोड़ा गया:

https://connect.microsoft.com/SQLServer/feedback/details/767196/order-by-datetime-in-odbc-fails-for-clean-sql-statements#details

मुझे यह भी ध्यान देना चाहिए कि हमने कार्यप्रणाली और गैर-कामकाजी प्रश्नों दोनों के लिए क्वेरी योजनाओं को देखा। वे दोनों निष्पादन गणना के आधार पर उचित रूप से उपयोग किए जाते हैं। कैश्ड प्लान को फ्लश करने और री-रन करने से क्वेरी की सफलता में बदलाव नहीं होता है।


यदि आप कोशिश करते हैं तो क्या होता है select id, test_date from [big table] where serial_number = ..... order by test_date- मैं सोच रहा हूं कि क्या SELECT *आपके प्रदर्शन पर नकारात्मक प्रभाव पड़ता है। यदि आपके पास एक गैर-अनुक्रमित सूचकांक है test_dateऔर उस पर एक संकुल सूचकांक id(यह मानते हुए कि इसे क्या कहा जाता है), इस क्वेरी को उस गैर-अनुक्रमित सूचकांक द्वारा कवर किया जाना चाहिए और इस तरह से बहुत जल्दी वापस आना चाहिए
marc_s

क्षमा करें, अच्छी बात है। मुझे इसमें शामिल होना चाहिए कि हमने विभिन्न संयोजनों के साथ चयनित स्तंभ स्थान ('*', आदि को हटाते हुए) को भारी रूप से संशोधित करने का प्रयास किया। ऊपर वर्णित व्यवहार उन परिवर्तनों के माध्यम से बना रहा।
DBtheDBA

मैंने अपने खाते अब उस साइट से जोड़ दिए हैं। यदि कोई मध्यस्थ उस साइट पर पोस्ट को स्थानांतरित करना चाहता है, तो मैं किसी भी तरह से ठीक हूं। मेरे डेवलपर्स में से एक ने मुझे यहाँ पोस्ट करने के बाद उस साइट की ओर इशारा किया।
DBtheDBA

यहां किस क्लाइंट स्टैक का उपयोग किया जा रहा है? पूरे ट्रेस टेक्स्ट के बिना, यह समस्या की तरह लगता है। मूल कॉल को अंदर लपेटकर sp_executesqlदेखें और देखें कि क्या होता है।
जॉन सेजेल

1
धीमी निष्पादन योजना क्या दिखती है? व्यास सूँघने?
मार्टिन स्मिथ

जवाबों:


6

कोई रहस्य नहीं है, आपको मूल रूप से यादृच्छिक पर एक अच्छा (एर) या (वास्तव में) खराब योजना मिलती है क्योंकि उपयोग करने के लिए सूचकांक के लिए कोई स्पष्ट कटौती विकल्प नहीं है। ORDER BY क्लॉज़ के लिए बाध्य करते हुए और इस प्रकार सॉर्ट से बचने के लिए, आप इस क्वेरी के लिए डेटाइम कॉलम पर नॉन-क्लस्टर्ड इंडेक्स बहुत खराब विकल्प है। इस क्वेरी के लिए एक बेहतर सूचकांक क्या होगा जो एक पर होगा (serial_number, test_date)। इससे भी बेहतर, यह क्लस्टर इंडेक्स कुंजी के लिए एक बहुत अच्छा उम्मीदवार बना देगा ।

अंगूठे के समय श्रृंखला के एक नियम के रूप में समय स्तंभ द्वारा क्लस्टर किया जाना चाहिए, क्योंकि अनुरोधों की अधिकता विशिष्ट समय सीमाओं में रुचि रखते हैं। यदि डेटा को स्वाभाविक रूप से कम चयनात्मकता वाले स्तंभ पर विभाजित किया जाता है, जैसे कि यह आपके serial_number के साथ मामला लगता है, तो इस कॉलम को क्लस्टर किए गए कुंजी परिभाषा में सबसे बाएं भाग के रूप में जोड़ा जाना चाहिए।


मैं यहाँ थोड़ा भ्रमित हूँ। the orderक्‍लॉज के आधार पर योजना क्‍यों होगी ? क्या whereयोजनाएँ केवल शर्तों तक ही सीमित नहीं होनी चाहिए क्योंकि आदेश केवल पंक्तियों को लाने के बाद होने चाहिए? सर्वर पूरे परिणाम सेट करने से पहले रिकॉर्ड करने की कोशिश क्यों करेगा?
DBtheDBA

5
यह भी नहीं बताता है कि क्वेरी की शुरुआत में टिप्पणी जोड़ने से रन अवधि प्रभावित होती है।
cfradenburg

इसके अलावा, हमारी तालिकाएँ लगभग हमेशा सीरियल नंबर से ही पूछी जाती हैं, test_date से नहीं। हम दोनों पर गैर-क्लस्टर किए गए अनुक्रमित हैं, और तालिका में केवल आईडी कॉलम पर क्लस्टर किया गया है। यह एक परिचालन डेटा स्टोर है, और अन्य स्तंभों पर क्लस्टर इंडेक्स जोड़ने से केवल पृष्ठ विभाजन और खराब प्रदर्शन होगा।
DBtheDBA

1
@DBtheDBA: यदि आप एक 'बग' के लिए दावा करना चाहते हैं, तो आपको एक उचित जांच और प्रकटीकरण करने की आवश्यकता है। आपकी तालिका और निर्यात किए गए आँकड़ों का सटीक स्कीमा, SQL Server 2005 और SQL Server 2008 में , विशेष रूप से सभी महत्वपूर्ण स्क्रिप्ट आँकड़े : स्क्रिप्ट सांख्यिकी और हिस्टोग्राम्स के लिए केवल-डेटाबेस डेटाबेस बनाने के लिए आवश्यक डेटाबेस मेटाडेटा की एक स्क्रिप्ट उत्पन्न करने के लिए कैसे का पालन करें । समस्या को पुन: उत्पन्न करने वाले चरणों के साथ पोस्ट जानकारी में जोड़ें।
रेमस रूसु

1
हमने पढ़ा है कि हमारी खोजों के दौरान, और मैं समझता हूं कि आप क्या कह रहे हैं, लेकिन सर्वर में जो कुछ कर रहा है उसमें एक मूलभूत दोष है। हमने तालिका, और अनुक्रमित को फिर से बनाया है, और इसे एक नई तालिका पर पुन: पेश किया है। Recompile विकल्प समस्या को ठीक नहीं करता है, जो एक बड़ा संकेत है जो कुछ गलत है। मुझे संदेह नहीं है कि हर चीज पर क्लस्टर इंडेक्स लगाने से यह समस्या ठीक हो सकती है, लेकिन इसका मूल कारण का समाधान नहीं है, यह एक वर्कअराउंड है, और एक बड़ी मेज पर एक महंगा है।
DBtheDBA

0

बग को कैसे पुन: उत्पन्न करें और इसे connect.microsoft.com पर सबमिट करने के विवरण का दस्तावेज़ दें। मैंने जाँच की और वहाँ पहले से ही कुछ भी नहीं देख सका जो इस से संबंधित होगा।


मैं अपने DBA को फिर से तैयार करने के लिए एक वातावरण बनाने के लिए एक स्क्रिप्ट टाइप करने के लिए कल मिलेगा। मुझे नहीं लगता कि यह इतना मुश्किल है। मैं इसे यहाँ पोस्ट करूँगा और साथ ही किसी को इसे स्वयं आज़माने में दिलचस्पी होनी चाहिए।
DBtheDBA

कनेक्ट होने पर आइटम को पोस्ट करें जब यह खोला जाता है। इस तरह से अगर किसी और के पास यह मुद्दा है तो वे इसे सही बताते हैं। और यह प्रश्न देखने वाला कोई भी व्यक्ति आइटम को वोट करना चाहता है, इसलिए Microsoft इस पर ध्यान देने की अधिक संभावना है।
cfradenburg

0

मेरी परिकल्पना यह है कि आप क्वेरी प्लान कैश से दूर चल रहे हैं। (रेमुस भले ही मैं जैसा हूं, वैसा ही कह रहा हूं।)

यहाँ पर विस्तार से एक टन है कि SQL कैशिंग की योजना कैसे बनाता है

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

बाद में, कोई व्यक्ति उसी क्वेरी को [कुछ संख्या] के भिन्न मान के लिए चला रहा है। यह विशिष्ट मान परिणाम पंक्तियों की बेतहाशा भिन्न संख्या में परिणत होता है और इंजन को क्वेरी के इस उदाहरण के लिए एक अलग योजना बनानी चाहिए। लेकिन यह इस तरह से काम नहीं करता है। इसके बजाय, SQL क्वेरी लेता है और (अधिक या कम) क्वेरी के पूर्व-मौजूदा संस्करण की तलाश में, क्वेरी कैश की खोज-संवेदनशील खोज करता है। जब यह पहले से एक पाता है, यह सिर्फ उस योजना का उपयोग करता है।

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

एक त्वरित उदाहरण: [लोगों से] का चयन करें जहां lastname = 'SMITH' - US GO में बहुत लोकप्रिय अंतिम नाम [लोगों] से चुनें जहां lastname = 'BONAPARTE' - यूएस में अंतिम लोकप्रिय नाम नहीं है

जब BONAPARTE के लिए क्वेरी चलाई जाती है, तो जो योजना SMITH के लिए बनाई गई थी, उसका फिर से उपयोग किया जाएगा। यदि SMITH एक टेबल स्कैन का कारण बनता है (जो अच्छा हो सकता है , यदि तालिका में पंक्तियाँ 99% SMITH हैं), तो BONAPARTE को टेबल स्कैन भी मिलेगा। यदि BONAPARTE को SMITH से पहले चलाया गया था, तो एक इंडेक्स का उपयोग करने वाली योजना का निर्माण और उपयोग किया जा सकता है, और फिर SMITH के लिए फिर से उपयोग किया जा सकता है (जो टेबल स्कैन के साथ बेहतर हो सकता है)। लोग यह नहीं देख सकते हैं कि एसएमईटीटी के लिए प्रदर्शन खराब है क्योंकि वे खराब प्रदर्शन की उम्मीद करते हैं क्योंकि पूरी तालिका को पढ़ना और सूचकांक को पढ़ना चाहिए और मेज पर hopping सीधे ध्यान नहीं दिया जाता है।

आपके परिवर्तन-उस-के-बदल-कुछ के संबंध में, मुझे संदेह है कि एसक्यूएल बस एक अलग योजना के रूप में देख रहा है और एक नई योजना बना रहा है, जो आपके [कुछ संख्या] के मूल्य के लिए विशिष्ट है।

इसका परीक्षण करने के लिए, क्वेरी के लिए एक निरर्थक परिवर्तन करें, जैसे कि फ़ॉर और टेबल नाम के बीच कुछ रिक्त स्थान जोड़ना, या अंत में एक टिप्पणी डालना। क्या यह तेज है? यदि हां, तो ऐसा इसलिए है क्योंकि यह प्रश्न कैश में जो है उससे थोड़ा अलग है, इसलिए एसक्यूएल ने "नए" प्रश्नों के लिए क्या किया।

एक समाधान के लिए, मैं तीन चीजों को देखूंगा। सबसे पहले, सुनिश्चित करें कि आपके आँकड़े अद्यतित हैं। यह वास्तव में पहली चीज होनी चाहिए जब आप एक अजीब या यादृच्छिक कार्य करते हैं। आपका डीबीए ऐसा होना चाहिए, लेकिन चीजें होती हैं। अप-टू-डेट आंकड़ों को सुनिश्चित करने का सामान्य तरीका अपनी तालिकाओं को फिर से अनुक्रमित करना है, जो जरूरी नहीं कि एक हल्की बात है, लेकिन केवल आंकड़ों को अपडेट करने के विकल्प भी हैं।

दूसरी बात यह है कि रेमुस के सुझावों की तर्ज पर सूचकांक को जोड़ना है। एक बेहतर / अलग सूचकांक के साथ, एक और बनाम एक मूल्य अधिक स्थिर हो सकता है और इतना बेतहाशा भिन्न नहीं हो सकता है।

यदि वह मदद नहीं करता है, तो कोशिश करने वाली तीसरी बात यह है कि हर बार जब आप स्टेटमेंट चलाते हैं, तो आप एक नया प्लान बना सकते हैं।

[बड़ी तालिका] से जहां * सीरियल_नंबर = [कुछ संख्या] का चयन करें, test_date desc OPTION (RIPPIL) द्वारा आदेश

यहाँ एक ऐसी ही स्थिति का वर्णन करने वाला एक लेख है । सच कहूँ तो, मैंने पहले केवल संग्रहीत प्रक्रियाओं पर लागू किए गए RECOMPILE को देखा था, लेकिन यह "नियमित" चयनित बयानों के साथ काम करता है। किम्बर्ली ट्रिप ने मुझे कभी गलत नहीं किया।

आप " प्लान गाइड " नामक सुविधा को भी देख सकते हैं , लेकिन यह अधिक जटिल है और ओवरकिल हो सकता है।


इनमें से कुछ चिंताओं को कवर करने के लिए: 1. आँकड़े अपडेट किए गए हैं, अपडेट किए जा रहे हैं। 2. हमने कई तरीकों से इंडेक्सिंग की कोशिश की है (इंडेक्स को कवर करते हुए, आदि) लेकिन समस्या order byविशेष रूप से डेटाइम इंडेक्स के खिलाफ उपयोग के लिए अधिक बंधी हुई लगती है । 3. बस RECOMPILE विकल्प के साथ अपने विचार की कोशिश की, यह अभी भी विफल रहा, जिसने मुझे थोड़ा आश्चर्यचकित किया, मैं उम्मीद कर रहा था कि यह काम करने जा रहा था, हालांकि मुझे नहीं पता कि क्या यह उत्पादन के लिए एक समाधान है।
DBtheDBA
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.