यह उन धार्मिक/राजनीतिक बहसों में से एक है जो वर्षों से चल रही है:क्या मुझे संग्रहीत प्रक्रियाओं का उपयोग करना चाहिए, या क्या मुझे अपने आवेदन में तदर्थ प्रश्न रखना चाहिए? मैं हमेशा कुछ कारणों से संग्रहित प्रक्रियाओं का समर्थक रहा हूं:
- यदि एप्लिकेशन कोड में क्वेरी बनाई गई है तो मैं SQL इंजेक्शन सुरक्षा लागू नहीं कर सकता। डेवलपर्स को पैरामीटरयुक्त प्रश्नों के बारे में पता हो सकता है लेकिन कुछ भी उन्हें ठीक से उपयोग करने के लिए मजबूर नहीं कर रहा है।
- मैं एप्लिकेशन स्रोत कोड में एम्बेड की गई क्वेरी को ट्यून नहीं कर सकता, न ही मैं किसी भी सर्वोत्तम अभ्यास को लागू कर सकता हूं।
- यदि मुझे क्वेरी ट्यूनिंग का अवसर मिलता है, तो इसे परिनियोजित करने के लिए, मुझे केवल संग्रहीत कार्यविधि को बदलने के बजाय, एप्लिकेशन कोड को फिर से संकलित करना और फिर से परिनियोजित करना होगा।
- यदि क्वेरी का उपयोग एप्लिकेशन में कई स्थानों पर, या कई अनुप्रयोगों में किया जाता है, और इसमें बदलाव की आवश्यकता होती है, तो मुझे इसे कई स्थानों पर बदलना होगा, जबकि एक संग्रहीत प्रक्रिया के साथ मुझे इसे केवल एक बार बदलना होगा (तैनाती के मुद्दे एक तरफ)।
मैं यह भी देखता हूं कि बहुत से लोग ओआरएम के पक्ष में संग्रहित प्रक्रियाओं को हटा रहे हैं। सरल अनुप्रयोगों के लिए यह शायद ठीक हो जाएगा, लेकिन जैसे-जैसे आपका आवेदन अधिक जटिल होता जाता है, आप पाएंगे कि आपकी पसंद का ओआरएम कुछ क्वेरी पैटर्न को निष्पादित करने में असमर्थ है, जो आपको संग्रहीत प्रक्रिया का उपयोग करने के लिए मजबूर करता है। यदि यह संग्रहीत कार्यविधियों का समर्थन करता है, अर्थात।
हालांकि मुझे अभी भी ये सभी तर्क बहुत आकर्षक लगते हैं, लेकिन वे वे नहीं हैं जिनके बारे में मैं आज बात करना चाहता हूं; मैं प्रदर्शन के बारे में बात करना चाहता हूं।
वहां बहुत सारे तर्क बस कहेंगे, "संग्रहीत प्रक्रियाएं बेहतर प्रदर्शन करती हैं!" यह कुछ बिंदु पर मामूली रूप से सही हो सकता है, लेकिन चूंकि SQL सर्वर ने ऑब्जेक्ट स्तर के बजाय कथन स्तर पर संकलन करने की क्षमता को जोड़ा है, और optimize for ad hoc workloads
जैसी शक्तिशाली कार्यक्षमता हासिल कर ली है। , यह अब बहुत मजबूत तर्क नहीं है। इंडेक्स ट्यूनिंग और समझदार क्वेरी पैटर्न का प्रदर्शन पर कभी भी संग्रहीत कार्यविधि का उपयोग करने की तुलना में बहुत अधिक प्रभाव पड़ता है; आधुनिक संस्करणों पर, मुझे संदेह है कि आपको ऐसे कई मामले मिलेंगे जहां एक ही क्वेरी ध्यान देने योग्य प्रदर्शन अंतर प्रदर्शित करती है, जब तक कि आप अन्य चर भी पेश नहीं कर रहे हों (जैसे स्थानीय रूप से एक प्रक्रिया बनाम एक अलग महाद्वीप पर एक अलग डेटा सेंटर में एक आवेदन)।
उस ने कहा, एक प्रदर्शन पहलू है जिसे अक्सर तदर्थ प्रश्नों से निपटने के दौरान अनदेखा किया जाता है:योजना कैश। हम optimize for ad hoc workloads
. का उपयोग कर सकते हैं एकल-उपयोग योजनाओं को हमारे कैश को भरने से रोकने के लिए (SQLskills.com के Kimberly Tripp (@KimberlyLTripp) में इसके बारे में कुछ अच्छी जानकारी है), और यह एकल-उपयोग योजनाओं को प्रभावित करता है, भले ही क्वेरी संग्रहीत प्रक्रिया के भीतर से चलाई गई हों या नहीं। या तदर्थ चलाए जा रहे हैं। एक अलग प्रभाव जिसे आप नोटिस नहीं कर सकते, इस सेटिंग पर ध्यान दिए बिना, समान . होने पर होता है SET
. में अंतर के कारण योजनाएं कैश में कई स्लॉट लेती हैं वास्तविक क्वेरी टेक्स्ट में विकल्प या मामूली डेल्टा। संपूर्ण "एप्लिकेशन में धीमा, SSMS में तेज़" परिघटना ने बहुत से लोगों को SET ARITHABORT
जैसी सेटिंग्स से जुड़े मुद्दों को हल करने में मदद की है। . आज मैं क्वेरी टेक्स्ट अंतर के बारे में बात करना चाहता था और कुछ ऐसा प्रदर्शित करना चाहता था जो हर बार जब मैं इसे लाता हूं तो लोगों को आश्चर्यचकित करता हूं।
कैश टू बर्न
मान लें कि हमारे पास AdventureWorks2012 चलाने वाला एक बहुत ही सरल सिस्टम है। और केवल यह साबित करने के लिए कि यह मदद नहीं करता है, हमने optimize for ad hoc workloads
सक्षम किया है :
EXEC sp_configure 'उन्नत विकल्प दिखाएं', 1;OVERRIDE के साथ GORECONFIGURE;GOEXEC sp_configure 'तदर्थ कार्यभार के लिए अनुकूलित करें', 1;ओवरराइड के साथ GORECONFIGURE;
और फिर योजना कैश मुक्त करें:
डीबीसीसी फ्रीप्रोकैच;
अब हम एक क्वेरी के लिए कुछ सरल विविधताएँ उत्पन्न करते हैं जो अन्यथा समान हैं। ये विविधताएं संभावित रूप से दो अलग-अलग डेवलपर्स के लिए कोडिंग शैलियों का प्रतिनिधित्व कर सकती हैं - सफेद स्थान में मामूली अंतर, ऊपरी/निचला मामला, आदि।
शीर्ष चुनें (1) SalesOrderID, OrderDate, SubTotalFROM Sales.SalesOrderHeaderWHERE SalesOrderID>=75120ORDER by OrderDate DESC;GO -- change>=75120 to> 75119 (समान तर्क चूंकि यह एक INT है) GO SELECT TOP (1) SalesOrderID, ऑर्डरडेट, सबटोटलफ्रॉम सेल्स.सेल्सऑर्डरहेडरजहां सेल्सऑर्डरआईडी> 75119ऑर्डर द्वारा ऑर्डरडेट डीईएससी;जाओ - क्वेरी को सभी लोअर केस में बदलेंGO टॉप (1) सेल्सऑर्डरिड, ऑर्डरडेट, सबटोटल से सेल्स.सेल्सऑर्डरहेडरजहां सेल्सऑर्डरआईडी> 75119ऑर्डरडेट द्वारा ऑर्डर करें;जाओ - चारों ओर कोष्ठक हटा दें टॉपजीओ के लिए तर्क शीर्ष 1 बिक्री क्रम का चयन करें, आदेश दिनांक, बिक्री से उपकुल। - अल्पविरामों के बीच रिक्त स्थान को हटा देंGO शीर्ष 1 बिक्री आदेश का चयन करें, आदेश दिनांक, बिक्री से उप-कुल। पूर्व>यदि हम उस बैच को एक बार चलाते हैं, और फिर योजना कैश की जांच करते हैं, तो हम देखते हैं कि हमारे पास अनिवार्य रूप से ठीक उसी निष्पादन योजना की 6 प्रतियां हैं। ऐसा इसलिए है क्योंकि क्वेरी टेक्स्ट बाइनरी हैशेड है, जिसका अर्थ है कि केस और व्हाइट स्पेस से फर्क पड़ता है और अन्यथा समान क्वेरीज़ SQL सर्वर के लिए अद्वितीय दिख सकती हैं।
चुनें [पाठ], size_in_bytes, usecounts, cacheobjtypeFROM sys.dm_exec_cached_plans as pCROSS APPLY sys.dm_exec_sql_text(p.plan_handle) AS tWHERE LOWER(t.[text]) LIKE '%ales.sales'+'orderheadedपरिणाम:
पाठ | size_in_bytes | उपयोग गणना | cacheobjtype |
---|---|---|---|
शीर्ष 1 बिक्री आदेश चुनें, ओ… | 272 | 1 | संकलित योजना आधार |
शीर्ष 1 बिक्री आदेश चुनें, ... | 272 | 1 | संकलित योजना आधार |
शीर्ष 1 बिक्री आदेश चुनें, ओ… | 272 | 1 | संकलित योजना आधार |
शीर्ष चुनें (1) salesorderid,... | 272 | 1 | संकलित योजना आधार |
शीर्ष चुनें (1) SalesOrderID,… | 272 | 1 | संकलित योजना आधार |
शीर्ष चुनें (1) SalesOrderID,… | 272 | 1 | संकलित योजना आधार |
"समान" प्रश्नों के पहले निष्पादन के बाद के परिणाम
इसलिए, यह पूरी तरह से बेकार नहीं है, क्योंकि तदर्थ सेटिंग ने SQL सर्वर को केवल पहले निष्पादन पर छोटे स्टब्स को स्टोर करने की अनुमति दी है। यदि हम फिर से बैच चलाते हैं (प्रक्रिया कैश को मुक्त किए बिना), तो हम थोड़ा अधिक खतरनाक परिणाम देखते हैं:
पाठ | size_in_bytes | उपयोग गणना | cacheobjtype |
---|---|---|---|
शीर्ष 1 बिक्री आदेश चुनें, ओ… | 49,152 | 1 | संकलित योजना |
शीर्ष 1 बिक्री आदेश चुनें, ... | 49,152 | 1 | संकलित योजना |
शीर्ष 1 बिक्री आदेश चुनें, ओ… | 49,152 | 1 | संकलित योजना |
शीर्ष चुनें (1) salesorderid,... | 49,152 | 1 | संकलित योजना |
शीर्ष चुनें (1) SalesOrderID,… | 49,152 | 1 | संकलित योजना |
शीर्ष चुनें (1) SalesOrderID,… | 49,152 | 1 | संकलित योजना |
"समान" क्वेरी के दूसरे निष्पादन के बाद के परिणाम
पैरामीटरयुक्त प्रश्नों के लिए भी यही बात होती है, भले ही पैरामीटरकरण सरल या मजबूर हो। और ऐसा ही तब होता है जब तदर्थ सेटिंग सक्षम नहीं होती है, सिवाय इसके कि यह जल्दी हो जाता है।
शुद्ध परिणाम यह है कि यह बहुत सारे प्लान कैश ब्लोट उत्पन्न कर सकता है, यहां तक कि समान दिखने वाले प्रश्नों के लिए भी - सभी तरह से दो प्रश्नों के लिए जहां एक डेवलपर एक टैब के साथ इंडेंट करता है और दूसरा 4 रिक्त स्थान के साथ इंडेंट करता है। मुझे आपको यह बताने की आवश्यकता नहीं है कि एक टीम में इस प्रकार की निरंतरता को लागू करने का प्रयास कठिन से लेकर असंभव तक कहीं भी हो सकता है। तो मेरे दिमाग में यह मॉड्यूलरिंग, डीआरवाई में देने और इस प्रकार की क्वेरी को एक संग्रहित प्रक्रिया में केंद्रीकृत करने के लिए एक मजबूत मंजूरी देता है।
एक चेतावनी
बेशक, यदि आप इस क्वेरी को एक संग्रहीत कार्यविधि में रखते हैं, तो आपके पास इसकी केवल एक प्रति होगी, इसलिए आप क्वेरी के कई संस्करणों को थोड़े भिन्न क्वेरी टेक्स्ट के साथ रखने की संभावना से पूरी तरह से बचते हैं। अब, आप यह भी तर्क दे सकते हैं कि अलग-अलग उपयोगकर्ता अलग-अलग नामों के साथ एक ही संग्रहीत कार्यविधि बना सकते हैं, और प्रत्येक संग्रहीत कार्यविधि में क्वेरी पाठ की थोड़ी भिन्नता होती है। जबकि संभव है, मुझे लगता है कि यह एक पूरी तरह से अलग समस्या का प्रतिनिधित्व करता है। :-)