कुछ हफ़्ते पहले, ट्विटर पर #SQLHelp हैश टैग पर ASYNC_NETWORK_IO प्रतीक्षा प्रकार पर निष्पादन योजनाओं के प्रभाव के बारे में एक दिलचस्प सवाल पूछा गया था, और इसने कुछ अलग राय और बहुत अच्छी चर्चा उत्पन्न की।
https://twitter.com/shawndube/status/1225476846537650176
इसका मेरा तत्काल उत्तर यह होगा कि कोई व्यक्ति इसके कारण और प्रभाव की गलत व्याख्या कर रहा है, क्योंकि ASYNC_NETWORK_IO प्रतीक्षा प्रकार का सामना तब होता है जब इंजन के पास क्लाइंट को टीडीएस भेजने के परिणाम होते हैं लेकिन कनेक्शन पर कोई टीडीएस बफर उपलब्ध नहीं होते हैं। पर। सामान्यतया, इसका अर्थ है कि ग्राहक पक्ष कुशलता से परिणामों का उपभोग नहीं कर रहा है, लेकिन आगामी चर्चा के आधार पर मैं कुछ परीक्षण करने के लिए पर्याप्त रूप से उत्सुक हो गया कि क्या निष्पादन योजना वास्तव में ASYNC_NETWORK_IO प्रतीक्षा को महत्वपूर्ण रूप से प्रभावित करेगी या नहीं।
संक्षेप में:ASYNC_NETWORK_IO पर ध्यान केंद्रित करना अकेले प्रतीक्षा करता है क्योंकि ट्यूनिंग मीट्रिक एक गलती है। कोई क्वेरी जितनी तेज़ी से निष्पादित होती है, यह प्रतीक्षा प्रकार उतना ही अधिक जमा होगा, भले ही क्लाइंट जितनी जल्दी हो सके परिणाम प्राप्त कर रहा हो। (सामान्य रूप से अकेले प्रतीक्षा पर ध्यान केंद्रित करने के बारे में ग्रेग की हालिया पोस्ट भी देखें।)
परीक्षण विन्यास
इसके लिए परीक्षण चलाने के लिए, एक उदाहरण के आधार पर एक बहुत ही सरल तालिका तैयार की गई थी जो मुझे समुदाय के किसी अन्य सदस्य से ईमेल द्वारा प्रदान की गई थी, जिसने प्रतीक्षा प्रकार में बदलाव का प्रदर्शन किया था, लेकिन दोनों के बीच एक पूरी तरह से अलग क्वेरी भी थी दूसरे परीक्षण में उपयोग की जा रही एक अतिरिक्त तालिका के साथ परीक्षण, और परिणामों को बंद करने के लिए इसमें एक टिप्पणी थी, जो इस प्रतीक्षा प्रकार के महत्वपूर्ण हिस्से को शुरू करने के लिए हटा देता है, इसलिए यह केवल एक योजना परिवर्तन अकेले नहीं है।
नोट:मैं यह बताना चाहूंगा कि यह किसी के प्रति नकारात्मक बयान नहीं है; आगामी चर्चा और आगे के परीक्षण जो प्रदान किए गए मूल प्रजनन से आए थे, वे बहुत ही शैक्षिक थे और इसने इस प्रतीक्षा प्रकार को समग्र रूप से समझने की दिशा में और शोध किया। मूल पुनरुत्पादन डीआईडी एक अंतर प्रदर्शित करता है, लेकिन अतिरिक्त परिवर्तनों के साथ जो मूल प्रश्न का हिस्सा नहीं थे जैसा कि प्रस्तुत किया गया था।
DROP TABLE IF EXISTS [DemoTable]; CREATE TABLE [DemoTable] ( ID INT PRIMARY KEY, FILLER VARCHAR(100) ); INSERT INTO [DemoTable] WITH (TABLOCK) SELECT TOP (250000) ROW_NUMBER() OVER (ORDER BY (SELECT NULL)), REPLICATE('Z', 50) FROM master..spt_values t1 CROSS JOIN master..spt_values t2 CROSS JOIN master..spt_values t3 OPTION (MAXDOP 1); GO
संकेतों का उपयोग करके विभिन्न योजना आकृतियों के परीक्षण के लिए इस तालिका को आधार डेटा सेट के रूप में उपयोग करते हुए, निम्नलिखित प्रश्नों का उपयोग किया गया था:
SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER HASH JOIN [DemoTable] t2 ON t1.ID = t2.ID; SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER MERGE JOIN [DemoTable] t2 ON t1.ID = t2.ID; SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER LOOP JOIN [DemoTable] t2 ON t1.ID = t2.ID;
चूंकि मैं SQL सर्वर 2019 CU1 पर इन प्रश्नों को चला रहा था, निष्पादन योजनाओं में क्वेरी निष्पादन से संबंधित वास्तविक प्रतीक्षा आँकड़े जानकारी शामिल थी।
नोट: अनुकूलक इस विशिष्ट डेटा सेट और क्वेरी के लिए संकेतों को लागू किए बिना मर्ज जॉइन का उपयोग करेगा।
प्रारंभिक परीक्षा परिणाम
प्रारंभिक परीक्षणों के लिए मैंने केवल प्रश्नों को चलाने के लिए SSMS का उपयोग किया और प्रत्येक क्वेरी से जुड़ी प्रतीक्षा जानकारी की तुलना करने के लिए वास्तविक निष्पादन योजना एकत्र की जो नीचे दिखाई गई है। ध्यान दें कि डेटा के इस आकार के लिए, बीता हुआ समय महत्वपूर्ण रूप से भिन्न नहीं है, और न ही ASYNC_NETWORK_IO के लिए प्रतीक्षा समय या प्रतीक्षा की गणना है।
हैश जॉइन
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="18393" WaitCount="8415" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="4394" WaitCount="6635" /> <Wait WaitType="HTDELETE" WaitTimeMs="957" WaitCount="6" /> <Wait WaitType="HTBUILD" WaitTimeMs="4" WaitCount="6" /> <Wait WaitType="HTREPARTITION" WaitTimeMs="3" WaitCount="6" /> <Wait WaitType="CMEMTHREAD" WaitTimeMs="3" WaitCount="14" /> <Wait WaitType="LATCH_EX" WaitTimeMs="2" WaitCount="8" /> </WaitStats> <QueryTimeStats CpuTime="1068" ElapsedTime="4961" />
शामिल हों
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3169" WaitCount="6592" /> </WaitStats> <QueryTimeStats CpuTime="792" ElapsedTime="3933" />
लूप जॉइन
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="13690" WaitCount="8286" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3576" WaitCount="6631" /> <Wait WaitType="LATCH_EX" WaitTimeMs="1" WaitCount="3" /> </WaitStats> <QueryTimeStats CpuTime="2172" ElapsedTime="4084" />
हालांकि, यह वह जगह नहीं थी जहां मैं परीक्षण रोकना चाहता था, क्योंकि मेरे अपने अनुभव ने बार-बार दिखाया है कि प्रबंधन स्टूडियो SQL सर्वर से परिणामों का एक बहुत ही अक्षम उपभोक्ता है और स्वयं कारण हो सकता है ASYNC_NETWORK_IO होने की प्रतीक्षा कर रहा है। इसलिए, मैंने यह बदलने का फैसला किया कि मैं चीजों का परीक्षण कैसे कर रहा था और प्रश्नों के SQLCMD निष्पादन पर गया।
SQLCMD के साथ परीक्षण
चूंकि मैं प्रस्तुत करते समय डेमो के लिए SQLCMD का बहुत उपयोग करता हूं, इसलिए मैंने निम्नलिखित सामग्री के साथ एक testscript.sql फ़ाइल बनाई:
PRINT 'Minimize Screen'; GO WAITFOR DELAY '00:00:05'; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER HASH JOIN [DemoTable] t2 ON t1.ID = t2.ID; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER MERGE JOIN [DemoTable] t2 ON t1.ID = t2.ID; GO SELECT t1.ID, t2.FILLER, t2.FILLER FROM [DemoTable] t1 INNER LOOP JOIN [DemoTable] t2 ON t1.ID = t2.ID; GO
इसे कमांड लाइन से निम्नानुसार निष्पादित किया गया था, और 5 सेकंड की देरी के दौरान विंडो को छोटा किया गया था ताकि निष्पादन को प्रसंस्करण के दौरान परिणाम प्रस्तुत न करने और स्क्रॉल करने की अनुमति मिल सके:
sqlcmd -S.\SQL2019 -i testscript.sql -dAdventureWorks2017वास्तविक निष्पादन योजनाओं पर कब्जा करने के लिए, मैं एक विस्तारित ईवेंट सत्र के साथ query_post_execution_showplan ईवेंट एकत्र कर रहा था, जो कि, SQL सर्वर 2019 पर, मैंने सोचा था कि मुझे हल्के क्वेरी निष्पादन आँकड़ों का उपयोग करने के लिए इन्फ्रास्ट्रक्चर v3 कार्यान्वयन का उपयोग करने के बजाय query_post_execution_plan_profile का उपयोग करना चाहिए था, लेकिन यह घटना WaitStats या QueryTimeStats जानकारी तब तक नहीं लौटाता जब तक कि query_post_execution_showplan भी उसी समय सक्षम न हो। इसके अतिरिक्त, चूंकि यह एक अलग परीक्षण मशीन है जिसमें कोई अन्य कार्यभार नहीं है, मानक प्रोफाइलिंग के प्रभाव वास्तव में यहां एक बड़ी चिंता का विषय नहीं हैं।
CREATE EVENT SESSION [Actual Plan] ON SERVER ADD EVENT sqlserver.query_post_execution_showplan (ACTION(sqlserver.session_id));
हैश जॉइन
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="45722" WaitCount="8674" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="11321" WaitCount="6610" /> <Wait WaitType="HTDELETE" WaitTimeMs="1174" WaitCount="6" /> <Wait WaitType="HTREPARTITION" WaitTimeMs="4" WaitCount="6" /> <Wait WaitType="HTBUILD" WaitTimeMs="3" WaitCount="5" /> <Wait WaitType="LATCH_EX" WaitTimeMs="2" WaitCount="7" /> </WaitStats> <QueryTimeStats ElapsedTime="11874" CpuTime="1070" />
शामिल हों
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="10837" WaitCount="6602" /> </WaitStats> <QueryTimeStats ElapsedTime="11597" CpuTime="789" />
लूप जॉइन
<WaitStats> <Wait WaitType="CXPACKET" WaitTimeMs="43587" WaitCount="8620" /> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="11177" WaitCount="6612" /> <Wait WaitType="LATCH_EX" WaitTimeMs="1" WaitCount="3" /> </WaitStats> <QueryTimeStats ElapsedTime="11696" CpuTime="2221" />
यह वास्तव में क्वेरी को निष्पादित करने का एक तेज़ तरीका नहीं था, और क्वेरी को निष्पादित करने के लिए कमांड लाइन उपयोगिता का उपयोग करके वास्तव में प्रदर्शन को कम कर दिया गया था, तब भी जब विंडो को छोटा किया गया हो और परिणामों को स्पष्ट रूप से स्क्रॉल नहीं किया गया हो। खिड़की खोलने के साथ HASH निष्पादन समय 15708ms था और ASYNC_NETWORK_IO प्रतीक्षा समय 15126ms था। हालांकि, यह दर्शाता है कि समान सटीक परिणामों के लिए परिणामों का उपभोग करने वाले क्लाइंट का प्रदर्शन क्वेरी के प्रतीक्षा समय और निष्पादन समय दोनों को प्रभावित करता है।
समानांतरता प्रभाव?
जिन चीजों पर मैंने गौर किया, उनमें से केवल दो योजनाओं को समानांतरता के साथ निष्पादित किया गया था, जो कि CXPACKET और LATCH_EX के अस्तित्व के आधार पर निष्पादन योजना XML में प्रतीक्षा करता है। इसलिए मैंने सोचा कि सीरियल निष्पादन योजना के लिए विकल्प (MAXDOP 1) का उपयोग करके इन्हीं प्रश्नों के निष्पादन पर किस तरह का प्रभाव पड़ेगा।
हैश जॉइन
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="4047" WaitCount="6379" /> </WaitStats> <QueryTimeStats CpuTime="602" ElapsedTime="4619" />
शामिल हों
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="3699" WaitCount="6608" /> </WaitStats> <QueryTimeStats CpuTime="810" ElapsedTime="4478" />
लूप जॉइन
<WaitStats> <Wait WaitType="ASYNC_NETWORK_IO" WaitTimeMs="2083" WaitCount="5385" /> </WaitStats> <QueryTimeStats CpuTime="1859" ElapsedTime="3918" />
यहां ध्यान दें कि कुल प्रतीक्षा संख्या में उल्लेखनीय कमी नहीं आई है। केवल सीरियल लूप जॉइन प्लान में प्रतीक्षा की संख्या या इससे जुड़े कुल प्रतीक्षा समय में एक बड़ा बदलाव होता है, और अलगाव में इसका मतलब यह नहीं है कि यह एक सकारात्मक लाभ है, क्वेरी के निष्पादन समय में काफी सुधार नहीं हुआ था और उस विशिष्ट परीक्षण के परिणामों को प्रभावित करने वाले अन्य कारक भी हो सकते हैं।
नीचे दी गई तालिका प्रत्येक परीक्षण के लिए ASYNC_NETWORK_IO प्रतीक्षा समय और गणना को सारांशित करती है।
प्लान टाइप | <थ> पंक्तियाँ <थ> प्रतीक्षा गणना <थ> प्रतीक्षा समयExecTime | <थ> ऐपनाम <थ> मैक्सडॉप 1समानांतर | |||||
---|---|---|---|---|---|---|---|
हैश | 250,000 | 6,635 | 4,394 | 4,961 | SSMS | N | Y |
मर्ज करें | 250,000 | 6,592 | 3,169 | 3,933 | SSMS | N | N |
लूप | 250,000 | 6,631 | 3,576 | 4,084 | SSMS | N | Y |
हैश | 250,000 | 6,610 | 11,321 | 11,874 | SQLCMD | N | Y |
मर्ज करें | 250,000 | 6,602 | 10,837 | 11,597 | SQLCMD | N | N |
लूप | 250,000 | 6,612 | 11,177 | 11,696 | SQLCMD | N | Y |
हैश | 250,000 | 6,379 | 4,047 | 4,619 | SSMS | Y | N |
मर्ज करें | 250,000 | 6,608 | 3,699 | 4,479 | SSMS | Y | N |
लूप | 250,000 | 5,385 | 2,083 | 3,918 | SSMS | Y | N |
सारांश
हालांकि इस पोस्ट की जांच में योजना परिवर्तन या ASYNC_NETWORK_IO प्रतीक्षा प्रकार के हर एक पहलू को शामिल नहीं किया गया है, यह प्रदर्शित करता है कि यह प्रतीक्षा निष्पादन योजना से महत्वपूर्ण रूप से प्रभावित नहीं होती है जिसका उपयोग क्वेरी के निष्पादन के लिए किया जाता है। मैं इस प्रतीक्षा प्रकार को लगभग CXPACKET प्रतीक्षा प्रकार की तरह वर्गीकृत करता हूँ जब एक सर्वर का समग्र रूप से विश्लेषण किया जाता है; अधिकांश कार्यभार के लिए देखना सामान्य है और जब तक कि यह अविश्वसनीय रूप से तिरछा न हो और क्लाइंट द्वारा परिणामों की धीमी खपत की ओर इशारा करते हुए अन्य प्रदर्शन समस्याएं हों, जैसे कि ASYNC_NETWORK_IO की प्रतीक्षा कर रहे लीड ब्लॉकर के साथ अवरुद्ध करना, फिर कुछ को 'सामान्य प्रतीक्षा हस्ताक्षर के हिस्से के रूप में अनदेखा किया जाना चाहिए। काम का बोझ'।