यह लंबे समय से स्थापित किया गया है कि बड़ी संख्या में पंक्तियों के साथ तालिका चर समस्याग्रस्त हो सकते हैं, क्योंकि अनुकूलक हमेशा उन्हें एक पंक्ति के रूप में देखता है। टेबल वेरिएबल के पॉप्युलेट होने के बाद बिना किसी रीकंपाइल के (क्योंकि इससे पहले यह खाली है), टेबल के लिए कोई कार्डिनैलिटी नहीं है, और ऑटोमैटिक रीकंपाइल नहीं होते हैं क्योंकि टेबल वेरिएबल रीकंपाइल थ्रेशोल्ड के अधीन भी नहीं होते हैं। इसलिए, योजनाएँ शून्य की तालिका कार्डिनैलिटी पर आधारित होती हैं, एक नहीं, बल्कि न्यूनतम को बढ़ाकर एक कर दिया जाता है, जैसा कि पॉल व्हाइट (@SQL_Kiwi) इस dba.stackexchange उत्तर में वर्णित करता है।
जिस तरह से हम आमतौर पर इस समस्या को हल कर सकते हैं वह है OPTION (RECOMPILE)
. जोड़ना तालिका चर को संदर्भित करने वाली क्वेरी के लिए, ऑप्टिमाइज़र को तालिका चर की कार्डिनैलिटी का निरीक्षण करने के लिए मजबूर करने के बाद इसे पॉप्युलेट किया गया है। जाने की आवश्यकता से बचने के लिए और एक स्पष्ट पुनर्संकलन संकेत जोड़ने के लिए प्रत्येक क्वेरी को मैन्युअल रूप से बदलने के लिए, SQL Server 2012 सर्विस पैक 2 और SQL Server 2014 संचयी अद्यतन #3 में एक नया ट्रेस फ़्लैग (2453) पेश किया गया है:
- KB #2952444 :FIX:SQL Server 2012 या SQL Server 2014 में तालिका चर का उपयोग करते समय खराब प्रदर्शन
जब ट्रेस फ्लैग 2453 सक्रिय होता है, तो टेबल वैरिएबल बनने के बाद ऑप्टिमाइज़र टेबल कार्डिनैलिटी का सटीक चित्र प्राप्त कर सकता है। यह बहुत सारे प्रश्नों के लिए एक अच्छी बात हो सकती है, लेकिन शायद सभी के लिए नहीं, और आपको पता होना चाहिए कि यह OPTION (RECOMPILE)
से अलग तरीके से कैसे काम करता है। . सबसे विशेष रूप से, पैरामीटर एम्बेडिंग ऑप्टिमाइज़ेशन पॉल व्हाइट इस पोस्ट में बात करता है OPTION (RECOMPILE)
के तहत होता है , लेकिन इस नए ट्रेस फ्लैग के तहत नहीं।
एक साधारण परीक्षण
मेरे प्रारंभिक परीक्षण में केवल एक तालिका चर को पॉप्युलेट करना और उसमें से चयन करना शामिल था; इसने 1 की सर्व-परिचित अनुमानित पंक्ति गणना प्राप्त की। यहाँ वह परीक्षण है जो मैंने चलाया (और मैंने तुलना करने के लिए पुन:संकलित संकेत जोड़ा):
DBCC TRACEON(2453); DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.id, t.name FROM @t AS t; SELECT t.id, t.name FROM @t AS t OPTION (RECOMPILE); DBCC TRACEOFF(2453);
SQL संतरी योजना एक्सप्लोरर का उपयोग करते हुए, हम देख सकते हैं कि इस मामले में दोनों प्रश्नों के लिए चित्रमय योजना समान है, शायद कम से कम आंशिक रूप से क्योंकि यह सचमुच एक तुच्छ योजना है:
@t के खिलाफ एक तुच्छ सूचकांक स्कैन के लिए ग्राफिकल योजना
हालांकि, अनुमान समान नहीं हैं। भले ही ट्रेस फ़्लैग सक्षम हो, फिर भी अगर हम पुन:संकलित संकेत का उपयोग नहीं करते हैं, तब भी हमें इंडेक्स स्कैन से 1 का अनुमान प्राप्त होता है:
स्टेटमेंट ग्रिड में एक तुच्छ योजना के अनुमानों की तुलना करना
ट्रेस फ्लैग (बाएं) और रीकंपाइल (दाएं) के बीच अनुमानों की तुलना करना
यदि आप कभी व्यक्तिगत रूप से मेरे आस-पास रहे हैं, तो आप शायद इस समय मेरे द्वारा बनाए गए चेहरे की कल्पना कर सकते हैं। मैंने निश्चित रूप से सोचा था कि या तो KB आलेख में गलत ट्रेस फ़्लैग नंबर सूचीबद्ध है, या यह कि मुझे वास्तव में सक्रिय होने के लिए किसी अन्य सेटिंग को सक्षम करने की आवश्यकता है।
बेंजामिन नेवारेज़ (@BenjaminNevarez) ने तुरंत मुझे बताया कि मुझे "SQL Server 2012 सर्विस पैक 2 में ठीक किए गए बग" KB आलेख को करीब से देखने की आवश्यकता है। हालांकि उन्होंने हाइलाइट्स> रिलेशनल इंजन के तहत छिपे हुए बुलेट के पीछे के टेक्स्ट को अस्पष्ट कर दिया है, फिक्स लिस्ट आर्टिकल मूल लेख (जोर मेरा) की तुलना में ट्रेस फ्लैग के व्यवहार का वर्णन करने में थोड़ा बेहतर काम करता है:
यदि कोई तालिका चर अन्य तालिकाओं के साथ जोड़ा जाता है SQL सर्वर में, यह अक्षम क्वेरी योजना चयन के कारण धीमा प्रदर्शन कर सकता है क्योंकि SQL सर्वर क्वेरी योजना को संकलित करते समय तालिका चर में आँकड़ों या पंक्तियों की संख्या का समर्थन नहीं करता है।तो यह इस विवरण से प्रकट होगा कि ट्रेस ध्वज केवल उस समस्या को हल करने के लिए है जब तालिका चर शामिल होने में भाग लेता है। (मूल लेख में यह अंतर क्यों नहीं बनाया गया है, मुझे नहीं पता।) लेकिन यह तब भी काम करता है जब हम प्रश्नों को थोड़ा और काम करते हैं - उपरोक्त क्वेरी को ऑप्टिमाइज़र द्वारा तुच्छ समझा जाता है, और ट्रेस फ़्लैग करता है ' उस मामले में कुछ भी करने की कोशिश भी नहीं करते। लेकिन अगर लागत-आधारित अनुकूलन किया जाता है, तो भी इसमें शामिल नहीं होगा; ट्रेस ध्वज का तुच्छ योजनाओं पर कोई प्रभाव नहीं पड़ता है। यहां एक गैर-तुच्छ योजना का उदाहरण दिया गया है जिसमें शामिल होना शामिल नहीं है:
DBCC TRACEON(2453); DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT TOP (100) t.id, t.name FROM @t AS t ORDER BY NEWID(); SELECT TOP (100) t.id, t.name FROM @t AS t ORDER BY NEWID() OPTION (RECOMPILE); DBCC TRACEOFF(2453);
यह योजना अब तुच्छ नहीं है; अनुकूलन को पूर्ण के रूप में चिह्नित किया गया है। लागत का बड़ा हिस्सा सॉर्ट ऑपरेटर को स्थानांतरित कर दिया जाता है:
कम तुच्छ आलेखीय योजना
और अनुमान दोनों प्रश्नों के लिए तैयार हैं (इस बार मैं आपको टूल टिप्स सहेज कर रखूंगा, लेकिन मैं आपको आश्वस्त कर सकता हूं कि वे वही हैं):
रिकंपाइल हिंट के साथ और बिना कम तुच्छ योजनाओं के लिए स्टेटमेंट ग्रिड
तो ऐसा लगता है कि केबी आलेख बिल्कुल सटीक नहीं है - मैं शामिल होने के बिना ट्रेस ध्वज से अपेक्षित व्यवहार को मजबूर करने में सक्षम था। लेकिन मैं इसे एक जॉइन के साथ भी परखना चाहता हूं।
बेहतर परीक्षण
आइए इस सरल उदाहरण को ट्रेस ध्वज के साथ और उसके बिना लें:
--DBCC TRACEON(2453); DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.name, c.name FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id]; --DBCC TRACEOFF(2453);
ट्रेस ध्वज के बिना, अनुकूलक का अनुमान है कि तालिका चर के विरुद्ध अनुक्रमणिका स्कैन से एक पंक्ति आएगी। हालाँकि, ट्रेस फ़्लैग सक्षम होने के साथ, यह 1,000 पंक्तियों को चालू कर देता है:
इंडेक्स स्कैन अनुमानों की तुलना (बाईं ओर कोई ट्रेस फ्लैग नहीं, ट्रेस ध्वज दाईं ओर)
मतभेद यहीं नहीं रुकते। यदि हम करीब से देखें, तो हम देख सकते हैं कि अनुकूलक द्वारा किए गए विभिन्न निर्णय इन बेहतर अनुमानों से उत्पन्न हुए हैं:
योजनाओं की तुलना (बाईं ओर कोई ट्रेस फ्लैग नहीं, ट्रेस फ्लैग दाईं ओर)
मतभेदों का एक त्वरित सारांश:
- ट्रेस फ़्लैग के बिना क्वेरी ने 4,140 रीड ऑपरेशन किए हैं, जबकि बेहतर अनुमान वाली क्वेरी ने केवल 424 (लगभग 90% की कमी) की है।
- ऑप्टिमाइज़र ने अनुमान लगाया कि पूरी क्वेरी ट्रेस फ़्लैग के बिना 10 पंक्तियाँ लौटाएगी, और ट्रेस फ़्लैग का उपयोग करते समय अधिक सटीक 2,318 पंक्तियाँ।
- ट्रेस फ्लैग के बिना, ऑप्टिमाइज़र ने नेस्टेड लूप जॉइन करना चुना (जो तब समझ में आता है जब इनपुट में से एक बहुत छोटा होने का अनुमान है)। इसने संयोजन ऑपरेटर को जन्म दिया और दोनों इंडेक्स ट्रेस फ्लैग के तहत चुने गए हैश मैच के विपरीत, 1,000 बार निष्पादित करना चाहता है, जहां कॉन्सटेनेशन ऑपरेटर और दोनों स्कैन केवल एक बार निष्पादित होते हैं।
- तालिका I/O टैब 1,000 स्कैन (इंडेक्स की तलाश के रूप में प्रच्छन्न श्रेणी स्कैन) और
syscolpars
के मुकाबले बहुत अधिक तार्किक पठन गणना भी दिखाता है (sys.all_columns
. के पीछे सिस्टम तालिका )। - जबकि अवधि महत्वपूर्ण रूप से प्रभावित नहीं हुई थी (24 मिलीसेकंड बनाम 18 मिलीसेकंड), आप शायद कल्पना कर सकते हैं कि इन अन्य अंतरों का अधिक गंभीर प्रश्न पर किस तरह का प्रभाव पड़ सकता है।
- यदि हम आरेख को अनुमानित लागतों पर स्विच करते हैं, तो हम देख सकते हैं कि ट्रेस फ़्लैग के बिना तालिका चर कितने भिन्न रूप से अनुकूलक को मूर्ख बना सकता है:
अनुमानित पंक्तियों की संख्या की तुलना करना (बाईं ओर कोई ट्रेस ध्वज नहीं, ट्रेस दाईं ओर झंडा)
यह स्पष्ट है और चौंकाने वाला नहीं है कि अनुकूलक सही योजना का चयन करने में बेहतर काम करता है जब इसमें शामिल कार्डिनैलिटी का सटीक दृष्टिकोण होता है। लेकिन किस कीमत पर?
रीकंपाइल और ओवरहेड
जब हम OPTION (RECOMPILE)
. का उपयोग करते हैं उपरोक्त बैच के साथ, ट्रेस फ़्लैग सक्षम किए बिना, हमें निम्न योजना मिलती है - जो ट्रेस फ़्लैग वाली योजना के समान है (केवल ध्यान देने योग्य अंतर यह है कि अनुमानित पंक्तियाँ 2,318 के बजाय 2,316 हैं):
विकल्प के साथ एक ही क्वेरी (RECOMPILE)
तो, यह आपको विश्वास दिला सकता है कि ट्रेस ध्वज हर बार आपके लिए एक पुन:संकलन को ट्रिगर करके समान परिणाम प्राप्त करता है। हम एक बहुत ही सरल विस्तारित ईवेंट सत्र का उपयोग करके इसकी जांच कर सकते हैं:
सर्वर परCREATE EVENT SESSION [CaptureRecompiles] ON SERVER ADD EVENT sqlserver.sql_statement_recompile ( ACTION(sqlserver.sql_text) ) ADD TARGET package0.asynchronous_file_target ( SET FILENAME = N'C:\temp\CaptureRecompiles.xel' ); GO ALTER EVENT SESSION [CaptureRecompiles] ON SERVER STATE = START;
मैंने बैचों के निम्नलिखित सेट को चलाया, जिसमें (ए) कोई पुन:संकलित विकल्प या ट्रेस ध्वज नहीं, (बी) पुन:संकलित विकल्प, और (सी) सत्र-स्तरीय ट्रेस ध्वज के साथ 20 प्रश्नों को निष्पादित किया गया।
/* default - no trace flag, no recompile */ DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.name, c.name FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id]; GO 20 /* recompile */ DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.name, c.name FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id] OPTION (RECOMPILE); GO 20 /* trace flag */ DBCC TRACEON(2453); DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.name, c.name FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id]; DBCC TRACEOFF(2453); GO 20
फिर मैंने इवेंट डेटा देखा:
SELECT sql_text = LEFT(sql_text, 255), recompile_count = COUNT(*) FROM ( SELECT x.x.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)') FROM sys.fn_xe_file_target_read_file(N'C:\temp\CaptureRecompiles*.xel',NULL,NULL,NULL) AS f CROSS APPLY (SELECT CONVERT(XML, f.event_data)) AS x(x) ) AS x(sql_text) GROUP BY LEFT(sql_text, 255);
परिणाम दिखाते हैं कि मानक क्वेरी के तहत कोई पुनर्संकलन नहीं हुआ, तालिका चर को संदर्भित करने वाला कथन एक बार पुन:संकलित किया गया था ट्रेस ध्वज के नीचे और, जैसा कि आप उम्मीद कर सकते हैं, हर बार RECOMPILE
. के साथ विकल्प:
sql_text | recompile_count |
---|---|
/* recompile */ DECLARE @t TABLE (i INT … | 20 |
/* ट्रेस फ्लैग */ DBCC TRACEON(2453); DECLARE @t … | 1 |
XEvents डेटा के विरुद्ध क्वेरी के परिणाम
इसके बाद, मैंने विस्तारित ईवेंट सत्र को बंद कर दिया, फिर पैमाने पर मापने के लिए बैच को बदल दिया। अनिवार्य रूप से कोड एक टेबल वेरिएबल बनाने और पॉप्युलेट करने के 1,000 पुनरावृत्तियों को मापता है, फिर इसके परिणामों को तीन विधियों में से प्रत्येक का उपयोग करके #temp तालिका (उस कई थ्रोअवे परिणामों के आउटपुट को दबाने का एक तरीका) में चुनता है।
SET NOCOUNT ON; /* default - no trace flag, no recompile */ SELECT SYSDATETIME(); GO DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.id, c.name INTO #x FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id]; DROP TABLE #x; GO 1000 SELECT SYSDATETIME(); GO /* recompile */ DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.id, c.name INTO #x FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id] OPTION (RECOMPILE); DROP TABLE #x; GO 1000 SELECT SYSDATETIME(); GO /* trace flag */ DBCC TRACEON(2453); DECLARE @t TABLE(id INT PRIMARY KEY, name SYSNAME NOT NULL UNIQUE); INSERT @t SELECT TOP (1000) [object_id], name FROM sys.all_objects; SELECT t.id, c.name INTO #x FROM @t AS t LEFT OUTER JOIN sys.all_columns AS c ON t.id = c.[object_id]; DROP TABLE #x; DBCC TRACEOFF(2453); GO 1000 SELECT SYSDATETIME(); GO
मैंने इस बैच को 10 बार चलाया और औसत लिया; वे थे:
विधि | औसत अवधि (मिलीसेकंड) |
---|---|
डिफ़ॉल्ट | 23,148.4 |
पुन:संकलित करें | 29,959.3 |
ट्रेस फ्लैग | 22,100.7 |
1,000 पुनरावृत्तियों की औसत अवधि
इस मामले में, हर बार पुन:संकलित संकेत का उपयोग करके सही अनुमान प्राप्त करना डिफ़ॉल्ट व्यवहार की तुलना में बहुत धीमा था, लेकिन ट्रेस ध्वज का उपयोग करना थोड़ा तेज था। यह समझ में आता है क्योंकि - जबकि दोनों विधियां नकली अनुमान का उपयोग करने के डिफ़ॉल्ट व्यवहार को सही करती हैं (और परिणामस्वरूप खराब योजना प्राप्त करना), पुन:संकलित संसाधन लेते हैं और, जब वे अधिक कुशल योजना नहीं देते हैं या नहीं कर सकते हैं समग्र बैच अवधि में योगदान करें।
सीधा लगता है, लेकिन रुकिए...
उपरोक्त परीक्षण थोड़ा - और जानबूझकर - त्रुटिपूर्ण है। हम टेबल वेरिएबल हर बार . में समान पंक्तियों (1,000) को सम्मिलित कर रहे हैं . क्या होता है यदि विभिन्न बैचों के लिए तालिका चर की प्रारंभिक जनसंख्या भिन्न होती है? निश्चित रूप से हम फिर से कंपाइल देखेंगे, यहां तक कि ट्रेस ध्वज के नीचे भी, है ना? एक और परीक्षण का समय। आइए एक अलग लक्ष्य फ़ाइल नाम के साथ थोड़ा अलग विस्तारित ईवेंट सत्र सेट करें (दूसरे सत्र के किसी भी डेटा को मिश्रित नहीं करने के लिए):
CREATE EVENT SESSION [CaptureRecompiles_v2] ON SERVER ADD EVENT sqlserver.sql_statement_recompile ( ACTION(sqlserver.sql_text) ) ADD TARGET package0.asynchronous_file_target ( SET FILENAME = N'C:\temp\CaptureRecompiles_v2.xel' ); GO ALTER EVENT SESSION [CaptureRecompiles_v2] ON SERVER STATE = START;
अब, इस बैच का निरीक्षण करते हैं, प्रत्येक पुनरावृत्ति के लिए पंक्ति गणना सेट करते हैं जो काफी भिन्न हैं। हम इसे तीन बार चलाएंगे, उपयुक्त टिप्पणियों को हटाते हुए ताकि हमारे पास एक बैच बिना ट्रेस ध्वज या स्पष्ट पुनर्संकलन के, एक बैच ट्रेस ध्वज के साथ, और एक बैच OPTION (RECOMPILE)
के साथ हो। (शुरुआत में सटीक टिप्पणी करने से इन बैचों को विस्तारित ईवेंट आउटपुट जैसी जगहों पर पहचानना आसान हो जाता है):
/* default, no trace flag or recompile */ /* recompile */ /* trace flag */ DECLARE @i INT = 1; WHILE @i <= 6 BEGIN --DBCC TRACEON(2453); -- uncomment this for trace flag DECLARE @t TABLE(id INT PRIMARY KEY); INSERT @t SELECT TOP (CASE @i WHEN 1 THEN 24 WHEN 2 THEN 1782 WHEN 3 THEN 1701 WHEN 4 THEN 12 WHEN 5 THEN 15 WHEN 6 THEN 1560 END) [object_id] FROM sys.all_objects; SELECT t.id, c.name FROM @t AS t INNER JOIN sys.all_objects AS c ON t.id = c.[object_id] --OPTION (RECOMPILE); -- uncomment this for recompile --DBCC TRACEOFF(2453); -- uncomment this for trace flag DELETE @t; SET @i += 1; END
मैंने इन बैचों को प्रबंधन स्टूडियो में चलाया, उन्हें प्लान एक्सप्लोरर में अलग-अलग खोला, और स्टेटमेंट ट्री को केवल SELECT
पर फ़िल्टर किया सवाल। अनुमानित और वास्तविक पंक्तियों को देखकर हम तीन बैचों में अलग-अलग व्यवहार देख सकते हैं:
तीन बैचों की तुलना, अनुमानित बनाम वास्तविक पंक्तियों को देखते हुए
सबसे दाहिने ग्रिड में, आप स्पष्ट रूप से देख सकते हैं कि ट्रेस फ़्लैग के अंतर्गत कहाँ पुनर्संकलन नहीं हुआ
हम यह देखने के लिए XEvents डेटा की जांच कर सकते हैं कि वास्तव में पुनर्संकलन के साथ क्या हुआ:
SELECT sql_text = LEFT(sql_text, 255), recompile_count = COUNT(*) FROM ( SELECT x.x.value(N'(event/action[@name="sql_text"]/value)[1]',N'nvarchar(max)') FROM sys.fn_xe_file_target_read_file(N'C:\temp\CaptureRecompiles_v2*.xel',NULL,NULL,NULL) AS f CROSS APPLY (SELECT CONVERT(XML, f.event_data)) AS x(x) ) AS x(sql_text) GROUP BY LEFT(sql_text, 255);
परिणाम:
sql_text | recompile_count |
---|---|
/* recompile */ DECLARE @i INT =1; जबकि … | 6 |
/* ट्रेस फ्लैग */ DECLARE @i INT =1; जबकि … | 4 |
XEvents डेटा के विरुद्ध क्वेरी के परिणाम
बहुत ही रोचक! ट्रेस फ्लैग के तहत, हम *करते हैं* पुन:संकलन देखते हैं, लेकिन केवल तभी जब रनटाइम पैरामीटर मान कैश्ड मान से महत्वपूर्ण रूप से भिन्न होता है। जब रनटाइम मान भिन्न होता है, लेकिन बहुत अधिक नहीं, तो हमें पुन:संकलन नहीं मिलता है, और समान अनुमानों का उपयोग किया जाता है। तो यह स्पष्ट है कि ट्रेस ध्वज तालिका चर के लिए एक पुन:संकलित सीमा का परिचय देता है, और मैंने पुष्टि की है (एक अलग परीक्षण के माध्यम से) कि यह उसी एल्गोरिदम का उपयोग करता है जैसा कि इस "प्राचीन" लेकिन अभी भी प्रासंगिक पेपर में #temp तालिकाओं के लिए वर्णित है। मैं इसे एक अनुवर्ती पोस्ट में साबित करूंगा।
हम फिर से प्रदर्शन का परीक्षण करेंगे, बैच को 1,000 बार चलाएंगे (विस्तारित ईवेंट सत्र बंद होने के साथ), और मापने की अवधि:
विधि | औसत अवधि (मिलीसेकंड) |
---|---|
डिफ़ॉल्ट | 101,285.4 |
पुन:संकलित करें | 111,423.3 |
ट्रेस फ्लैग | 110,318.2 |
1,000 पुनरावृत्तियों की औसत अवधि
इस विशिष्ट परिदृश्य में, हम हर बार एक पुन:संकलित करने के लिए या ट्रेस ध्वज का उपयोग करके प्रदर्शन का लगभग 10% खो देते हैं। निश्चित नहीं है कि डेल्टा कैसे वितरित किया गया था:क्या योजनाएं बेहतर अनुमानों पर आधारित थीं काफी . नहीं बेहतर? क्या रीकंपाइल ने किसी भी प्रदर्शन लाभ को इतना से ऑफसेट किया था ? मैं इस पर ज्यादा समय नहीं देना चाहता, और यह एक छोटा सा उदाहरण था, लेकिन यह आपको दिखाता है कि अनुकूलक के काम करने के तरीके के साथ खेलना एक अप्रत्याशित मामला हो सकता है। कभी-कभी आप कार्डिनैलिटी =1 के डिफ़ॉल्ट व्यवहार से बेहतर हो सकते हैं, यह जानते हुए कि आप कभी भी किसी भी अनुचित पुनर्संकलन का कारण नहीं बनेंगे। जहाँ ट्रेस फ़्लैग बहुत मायने रखता है, यदि आपके पास ऐसे प्रश्न हैं जहाँ आप बार-बार डेटा के एक ही सेट के साथ तालिका चर को पॉप्युलेट कर रहे हैं (जैसे, एक पोस्टल कोड लुकअप टेबल) या आप हमेशा 50 या 1,000 पंक्तियों का उपयोग कर रहे हैं (कहते हैं, पॉप्युलेटिंग) पेजिनेशन में उपयोग के लिए एक टेबल वैरिएबल)। किसी भी मामले में, आपको निश्चित रूप से किसी भी कार्यभार पर इसके प्रभाव का परीक्षण करना चाहिए जहां आप ट्रेस ध्वज या स्पष्ट पुनर्संकलन शुरू करने की योजना बना रहे हैं।
टीवीपी और टेबल प्रकार
मैं भी उत्सुक था कि यह तालिका प्रकारों को कैसे प्रभावित करेगा, और क्या हम टीवीपी के लिए कार्डिनैलिटी में कोई सुधार देखेंगे, जहां यह समान लक्षण मौजूद है। इसलिए मैंने एक साधारण तालिका प्रकार बनाया जो अब तक उपयोग में आने वाले तालिका चर की नकल करता है:
USE MyTestDB; GO CREATE TYPE dbo.t AS TABLE ( id INT PRIMARY KEY );
फिर मैंने उपरोक्त बैच लिया और बस DECLARE @t TABLE(id INT PRIMARY KEY);
DECLARE @t dbo.t;
. के साथ - बाकी सब कुछ वैसा ही रहा। मैंने वही तीन बैच चलाए, और जो मैंने देखा वह है:
डिफ़ॉल्ट व्यवहार, विकल्प रीकंपाइल और ट्रेस फ्लैग के बीच अनुमानों और वास्तविक की तुलना करना 2453
तो हाँ, ऐसा लगता है कि ट्रेस फ़्लैग टीवीपी के साथ ठीक उसी तरह काम करता है - जब पंक्ति गणना पुनर्संकलन सीमा से अधिक हो जाती है, तो पुनर्संकलन ऑप्टिमाइज़र के लिए नए अनुमान उत्पन्न करता है, और जब पंक्ति गणना "काफी करीब" होती है तो छोड़ दी जाती है।
पेशेवरों, विपक्ष और चेतावनियों
ट्रेस फ़्लैग का एक फ़ायदा यह है कि आप कुछ . से बच सकते हैं पुन:संकलित करता है और अभी भी तालिका कार्डिनैलिटी देखता है - जब तक आप तालिका चर में पंक्तियों की संख्या स्थिर होने की अपेक्षा करते हैं, या अलग-अलग कार्डिनैलिटी के कारण महत्वपूर्ण योजना विचलन का निरीक्षण नहीं करते हैं। दूसरा यह है कि आप इसे विश्व स्तर पर या सत्र स्तर पर सक्षम कर सकते हैं और आपको अपने सभी प्रश्नों के लिए पुन:संकलित संकेतों को पेश करने की आवश्यकता नहीं है। और अंत में, कम से कम उस मामले में जहां तालिका परिवर्तनीय कार्डिनैलिटी स्थिर थी, उचित अनुमानों ने डिफ़ॉल्ट से बेहतर प्रदर्शन किया, और रीकंपाइल विकल्प का उपयोग करने से बेहतर प्रदर्शन भी किया - वे सभी संकलन निश्चित रूप से जोड़ सकते हैं।
बेशक, कुछ नुकसान भी हैं। एक जिसका मैंने ऊपर उल्लेख किया है वह यह है कि OPTION (RECOMPILE)
. की तुलना में आप कुछ अनुकूलन से चूक जाते हैं, जैसे कि पैरामीटर एम्बेडिंग। दूसरा यह है कि ट्रेस फ़्लैग का वह प्रभाव नहीं होगा जिसकी आप तुच्छ योजनाओं पर अपेक्षा करते हैं। और रास्ते में मैंने जो खोजा वह यह है कि QUERYTRACEON
. का उपयोग करना क्वेरी स्तर पर ट्रेस ध्वज को लागू करने का संकेत काम नहीं करता है - जहां तक मैं बता सकता हूं, ट्रेस ध्वज उस स्थान पर होना चाहिए जब तालिका चर या टीवीपी बनाया जाता है और/या अनुकूलक को ऊपर कार्डिनैलिटी देखने के लिए पॉप्युलेट किया जाता है 1.
ध्यान रखें कि वैश्विक स्तर पर ट्रेस फ़्लैग को चलाने से तालिका चर से जुड़ी किसी भी क्वेरी के लिए क्वेरी प्लान रिग्रेशन की संभावना का परिचय मिलता है (यही कारण है कि यह सुविधा पहली बार ट्रेस फ़्लैग के तहत पेश की गई थी), इसलिए कृपया अपने संपूर्ण कार्यभार का परीक्षण करना सुनिश्चित करें। कोई फर्क नहीं पड़ता कि आप ट्रेस ध्वज का उपयोग कैसे करते हैं। साथ ही, जब आप इस व्यवहार का परीक्षण कर रहे हों, तो कृपया इसे उपयोगकर्ता डेटाबेस में करें; कुछ अनुकूलन और सरलीकरण जो आप सामान्य रूप से होने की उम्मीद करते हैं, जब संदर्भ tempdb पर सेट होता है तो ऐसा नहीं होता है, इसलिए जब आप कोड और सेटिंग्स को उपयोगकर्ता डेटाबेस में ले जाते हैं तो कोई भी व्यवहार सुसंगत नहीं रह सकता है।
निष्कर्ष
यदि आप बड़ी लेकिन अपेक्षाकृत सुसंगत पंक्तियों के साथ तालिका चर या टीवीपी का उपयोग करते हैं, तो आपको कुछ बैचों या प्रक्रियाओं के लिए इस ट्रेस ध्वज को सक्षम करना फायदेमंद हो सकता है ताकि व्यक्तिगत प्रश्नों पर मैन्युअल रूप से पुन:संकलित किए बिना सटीक तालिका कार्डिनैलिटी प्राप्त हो सके। आप इंस्टेंस स्तर पर ट्रेस ध्वज का भी उपयोग कर सकते हैं, जो सभी प्रश्नों को प्रभावित करेगा। लेकिन किसी भी बदलाव की तरह, किसी भी मामले में, आपको अपने पूरे कार्यभार के प्रदर्शन का परीक्षण करने, किसी भी प्रतिगमन के लिए स्पष्ट रूप से देखने और यह सुनिश्चित करने की आवश्यकता होगी कि आप ट्रेस ध्वज व्यवहार चाहते हैं क्योंकि आप अपनी तालिका चर की स्थिरता पर भरोसा कर सकते हैं पंक्ति मायने रखती है।
मुझे SQL सर्वर 2014 में जोड़े गए ट्रेस फ्लैग को देखकर खुशी हुई, लेकिन यह बेहतर होगा कि यह सिर्फ डिफ़ॉल्ट व्यवहार बन जाए। ऐसा नहीं है कि बड़ी #temp तालिकाओं पर बड़े तालिका चर का उपयोग करने का कोई महत्वपूर्ण लाभ है, लेकिन इन दो अस्थायी संरचना प्रकारों के बीच अधिक समानता देखना अच्छा होगा जिन्हें उच्च स्तर पर निर्धारित किया जा सकता है। हमारे पास जितनी अधिक समानता होगी, उतने ही कम लोगों को इस बात पर विचार करना होगा कि उन्हें किसका उपयोग करना चाहिए (या चुनने पर विचार करने के लिए कम से कम मानदंड हैं)। मार्टिन स्मिथ के पास dba.stackexchange पर एक शानदार प्रश्नोत्तर ओवर है जो शायद अब एक अपडेट के कारण है:SQL सर्वर में एक अस्थायी तालिका और तालिका चर के बीच क्या अंतर है?
महत्वपूर्ण नोट
यदि आप SQL Server 2012 सर्विस पैक 2 स्थापित करने जा रहे हैं (इस ट्रेस ध्वज का उपयोग करना है या नहीं), तो कृपया SQL Server 2012 और 2014 में एक प्रतिगमन के बारे में मेरी पोस्ट भी देखें जो दुर्लभ परिदृश्यों में - परिचय दे सकती है ऑनलाइन इंडेक्स के पुनर्निर्माण के दौरान संभावित डेटा हानि या भ्रष्टाचार। SQL Server 2012 SP1 और SP2 और SQL Server 2014 के लिए भी संचयी अद्यतन उपलब्ध हैं। 2012 RTM शाखा के लिए कोई समाधान नहीं होगा।
आगे परीक्षण
मेरे पास परीक्षण करने के लिए मेरी सूची में अन्य चीजें हैं। एक के लिए, मैं यह देखना चाहता हूं कि SQL सर्वर 2014 में इन-मेमोरी टेबल प्रकारों पर इस ट्रेस ध्वज का कोई प्रभाव पड़ता है या नहीं। मैं एक संदेह की छाया से परे साबित करने जा रहा हूं कि ट्रेस ध्वज 2453 तालिका के लिए समान पुनर्संयोजन सीमा का उपयोग करता है वेरिएबल और TVPs जैसा कि यह #temp टेबल के लिए करता है।