Sqlserver
 sql >> डेटाबेस >  >> RDS >> Sqlserver

SQL सर्वर 2019 में स्केलर UDF इनलाइनिंग

स्केलर यूडीएफ हमेशा एक दोधारी तलवार रहे हैं - वे डेवलपर्स के लिए बहुत अच्छे हैं, जो अपने सभी प्रश्नों को दोहराने के बजाय थकाऊ तर्क को दूर कर देते हैं, लेकिन वे उत्पादन में रनटाइम प्रदर्शन के लिए भयानक हैं, क्योंकि अनुकूलक करता है ' टी उन्हें अच्छी तरह से संभालें। अनिवार्य रूप से क्या होता है कि यूडीएफ निष्पादन शेष निष्पादन योजना से अलग रखा जाता है, और इसलिए उन्हें प्रत्येक पंक्ति के लिए एक बार बुलाया जाता है और अनुमानित या वास्तविक पंक्तियों की संख्या के आधार पर अनुकूलित नहीं किया जा सकता है या शेष योजना में जोड़ दिया जा सकता है।

चूंकि, SQL Server 2000 के बाद से हमारे सर्वोत्तम प्रयासों के बावजूद, हम स्केलर UDFs को प्रभावी रूप से उपयोग होने से नहीं रोक सकते हैं, क्या SQL सर्वर को केवल उन्हें बेहतर तरीके से संभालने के लिए यह बहुत अच्छा नहीं होगा?

SQL सर्वर 2019 स्केलर UDF इनलाइनिंग नामक एक नई सुविधा पेश करता है। समारोह को अलग रखने के बजाय इसे समग्र योजना में शामिल किया जाता है। यह एक बेहतर निष्पादन योजना और बदले में, बेहतर रनटाइम प्रदर्शन की ओर ले जाता है।

लेकिन पहले, समस्या के स्रोत को बेहतर ढंग से समझाने के लिए, SQL सर्वर 2017 (या 2019 पर लेकिन कम संगतता स्तर के साथ) पर चलने वाले डेटाबेस में, कुछ पंक्तियों के साथ सरल तालिकाओं की एक जोड़ी के साथ शुरू करते हैं:

CREATE DATABASE Whatever;
GO
ALTER DATABASE Whatever SET COMPATIBILITY_LEVEL = 140;
GO
USE Whatever;
GO
 
CREATE TABLE dbo.Languages
(
  LanguageID int PRIMARY KEY,
  Name sysname
);
 
CREATE TABLE dbo.Employees
(
  EmployeeID int PRIMARY KEY,
  LanguageID int NOT NULL FOREIGN KEY REFERENCES dbo.Languages(LanguageID)
);
 
INSERT dbo.Languages(LanguageID, Name) VALUES(1033, N'English'), (45555, N'Klingon');
 
INSERT dbo.Employees(EmployeeID, LanguageID)
  SELECT [object_id], CASE ABS([object_id]%2) WHEN 1 THEN 1033 ELSE 45555 END 
  FROM sys.all_objects;

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

CREATE FUNCTION dbo.GetLanguage(@id int)
RETURNS sysname
AS
BEGIN
  RETURN (SELECT Name FROM dbo.Languages WHERE LanguageID = @id);
END

तब हमारी वास्तविक क्वेरी कुछ इस तरह दिखती है:

SELECT TOP (6) EmployeeID, Language = dbo.GetLanguage(LanguageID)
  FROM dbo.Employees;

यदि हम क्वेरी के लिए निष्पादन योजना को देखें, तो कुछ अजीब तरह से गायब है:

निष्पादन योजना कर्मचारियों तक पहुंच दिखाती है लेकिन भाषाओं तक नहीं

भाषा तालिका को कैसे एक्सेस किया जाता है? यह योजना बहुत ही कुशल दिखती है क्योंकि - फ़ंक्शन की तरह ही - इसमें शामिल कुछ जटिलताओं को दूर कर रहा है। वास्तव में, यह आलेखीय योजना एक क्वेरी के समान है जो Language को केवल एक स्थिरांक या चर निर्दिष्ट करती है कॉलम:

SELECT TOP (6) EmployeeID, Language = N'Sanskrit'
  FROM dbo.Employees;

लेकिन यदि आप मूल क्वेरी के विरुद्ध एक ट्रेस चलाते हैं, तो आप देखेंगे कि मुख्य क्वेरी के अलावा फ़ंक्शन में वास्तव में छह कॉल (प्रत्येक पंक्ति के लिए एक) हैं, लेकिन ये योजनाएँ SQL सर्वर द्वारा वापस नहीं की जाती हैं।

आप sys.dm_exec_function_stats . की जांच करके भी इसे सत्यापित कर सकते हैं , लेकिन यह कोई गारंटी नहीं है :

SELECT [function] = OBJECT_NAME([object_id]), execution_count 
  FROM sys.dm_exec_function_stats
  WHERE object_name(object_id) IS NOT NULL;
function         execution_count
-----------      ---------------
GetLanguage                    6

यदि आप उत्पाद के भीतर से एक वास्तविक योजना तैयार करते हैं तो SentryOne Plan Explorer विवरण दिखाएगा, लेकिन हम उन्हें केवल ट्रेस से प्राप्त कर सकते हैं, और व्यक्तिगत फ़ंक्शन कॉल के लिए अभी भी कोई योजना एकत्र या प्रदर्शित नहीं की गई है:

व्यक्तिगत अदिश UDF आह्वान के लिए विवरण ट्रेस करें

यह सब उन्हें समस्या निवारण के लिए बहुत कठिन बना देता है, क्योंकि आपको उनका शिकार करना पड़ता है, तब भी जब आप पहले से ही जानते हैं कि वे वहां हैं। यदि आप अनुमानित लागत जैसी चीजों के आधार पर दो योजनाओं की तुलना कर रहे हैं, तो यह प्रदर्शन विश्लेषण की एक वास्तविक गड़बड़ी भी कर सकता है, क्योंकि न केवल संबंधित ऑपरेटर भौतिक आरेख से छिपे हुए हैं, लागत को योजना में कहीं भी शामिल नहीं किया गया है।

एसक्यूएल सर्वर 2019 पर फास्ट फॉरवर्ड करें

इन सभी वर्षों के समस्याग्रस्त व्यवहार और अस्पष्ट मूल कारणों के बाद, उन्होंने इसे बनाया है ताकि कुछ कार्यों को समग्र निष्पादन योजना में अनुकूलित किया जा सके। स्केलर यूडीएफ इनलाइनिंग उन वस्तुओं को बनाता है जिन्हें वे समस्या निवारण के लिए दृश्यमान बनाते हैं *और* उन्हें निष्पादन योजना रणनीति में जोड़ने की अनुमति देता है। अब कार्डिनैलिटी अनुमान (आंकड़ों के आधार पर) उन रणनीतियों में शामिल होने की अनुमति देते हैं जो हर पंक्ति के लिए फ़ंक्शन को एक बार कॉल किए जाने पर संभव नहीं थे।

हम ऊपर के समान उदाहरण का उपयोग कर सकते हैं, या तो SQL सर्वर 2019 डेटाबेस पर ऑब्जेक्ट्स का एक ही सेट बना सकते हैं, या प्लान कैश को साफ़ कर सकते हैं और संगतता स्तर को 150 तक बढ़ा सकते हैं:

ALTER DATABASE SCOPED CONFIGURATION CLEAR PROCEDURE_CACHE;
GO
ALTER DATABASE Whatever SET COMPATIBILITY_LEVEL = 150;
GO

अब जब हम अपनी छह-पंक्ति क्वेरी फिर से चलाते हैं:

SELECT TOP (6) EmployeeID, Language = dbo.GetLanguage(LanguageID)
  FROM dbo.Employees;

हमें एक योजना मिलती है जिसमें भाषा तालिका और उस तक पहुंचने से जुड़ी लागतें शामिल होती हैं:

योजना जिसमें अदिश UDF में संदर्भित वस्तुओं तक पहुंच शामिल है

यहां ऑप्टिमाइज़र ने नेस्टेड लूप जॉइन को चुना, लेकिन अलग-अलग परिस्थितियों में, यह एक अलग जॉइन स्ट्रैटेजी चुन सकता था, समांतरता पर विचार कर सकता था, और योजना के आकार को पूरी तरह से बदलने के लिए अनिवार्य रूप से स्वतंत्र था। आपको इसे किसी ऐसी क्वेरी में देखने की संभावना नहीं है जो 6 पंक्तियों को लौटाती है और यह किसी भी तरह से एक प्रदर्शन समस्या नहीं है, लेकिन बड़े पैमाने पर यह हो सकता है।

योजना दर्शाती है कि फ़ंक्शन को प्रति पंक्ति नहीं कहा जा रहा है - जबकि तलाश वास्तव में छह बार निष्पादित की जाती है, आप देख सकते हैं कि फ़ंक्शन अब sys.dm_exec_function_stats में दिखाई नहीं देता है . एक नकारात्मक पहलू जो आप दूर कर सकते हैं, वह यह है कि, यदि आप इस DMV का उपयोग यह निर्धारित करने के लिए करते हैं कि क्या कोई फ़ंक्शन सक्रिय रूप से उपयोग किया जा रहा है (जैसा कि हम अक्सर प्रक्रियाओं और अनुक्रमित के लिए करते हैं), तो यह अब विश्वसनीय नहीं होगा।

चेतावनी

प्रत्येक स्केलर फ़ंक्शन इनलाइन करने योग्य नहीं होता है और यहां तक ​​​​कि जब कोई फ़ंक्शन * इनलाइन करने योग्य होता है, तो यह जरूरी नहीं कि हर परिदृश्य में इनलाइन हो। इसे अक्सर फ़ंक्शन की जटिलता, शामिल क्वेरी की जटिलता, या दोनों के संयोजन के साथ करना पड़ता है। आप जांच सकते हैं कि कोई फ़ंक्शन sys.sql_modules . में इनलाइन करने योग्य है या नहीं कैटलॉग व्यू:

SELECT OBJECT_NAME([object_id]), definition, is_inlineable
  FROM sys.sql_modules;

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

ALTER FUNCTION dbo.GetLanguage(@id int)
RETURNS sysname
WITH INLINE = OFF
AS
BEGIN
  RETURN (SELECT Name FROM dbo.Languages WHERE LanguageID = @id);
END
GO

और आप इसे डेटाबेस स्तर पर नियंत्रित कर सकते हैं, लेकिन संगतता स्तर से अलग:

ALTER DATABASE SCOPED CONFIGURATION SET TSQL_SCALAR_UDF_INLINING = OFF;

हालांकि उस हथौड़ा, आईएमएचओ को स्विंग करने के लिए आपके पास एक बहुत अच्छा उपयोग केस होना चाहिए।

निष्कर्ष

अब, मैं सुझाव नहीं दे रहा हूं कि आप तर्क के हर टुकड़े को एक स्केलर यूडीएफ में दूर कर सकते हैं और यह मान सकते हैं कि अब SQL सर्वर सभी मामलों का ख्याल रखेगा। यदि आपके पास बहुत अधिक स्केलर यूडीएफ उपयोग वाला डेटाबेस है, तो आपको नवीनतम SQL सर्वर 2019 CTP डाउनलोड करना चाहिए, वहां अपने डेटाबेस का बैकअप पुनर्स्थापित करना चाहिए, और यह देखने के लिए DMV की जांच करें कि समय आने पर उनमें से कितने फ़ंक्शन इनलाइन करने योग्य होंगे। अगली बार जब आप अपग्रेड के लिए बहस कर रहे हों तो यह एक प्रमुख बुलेट पॉइंट हो सकता है, क्योंकि आपको अनिवार्य रूप से वह सारा प्रदर्शन मिल जाएगा और समस्या निवारण समय बर्बाद हो जाएगा।

इस बीच, यदि आप स्केलर यूडीएफ प्रदर्शन से पीड़ित हैं और आप जल्द ही किसी भी समय SQL सर्वर 2019 में अपग्रेड नहीं करेंगे, तो समस्या को कम करने में मदद करने के अन्य तरीके भी हो सकते हैं।

ध्यान दें:इससे पहले कि मुझे एहसास हुआ कि मैंने पहले ही एक अलग टुकड़ा कहीं और पोस्ट कर दिया है, मैंने इस लेख को लिखा और कतारबद्ध किया।


  1. Database
  2.   
  3. Mysql
  4.   
  5. Oracle
  6.   
  7. Sqlserver
  8.   
  9. PostgreSQL
  10.   
  11. Access
  12.   
  13. SQLite
  14.   
  15. MariaDB
  1. पंक्ति 1, कॉलम 4 (वर्ष) के लिए बल्क लोड डेटा रूपांतरण त्रुटि (निर्दिष्ट कोडपेज के लिए बेमेल या अमान्य वर्ण टाइप करें)

  2. SQL सर्वर में सत्र संदर्भ में कुंजी/मान जोड़े सेट करें (sp_set_session_context)

  3. जांचें कि क्या तालिका SQL सर्वर में मौजूद है

  4. फ़ाइल में छवि फ़ील्ड कैसे निर्यात करें?

  5. क्या प्राथमिक कुंजी निष्क्रिय हैं?