SQL सर्वर में किसी समस्या के निवारण में गहराई से शामिल होने पर, कभी-कभी आप स्वयं को उस सटीक क्रम को जानना चाहते हैं जिसमें क्वेरी चलती है। मैं इसे अधिक जटिल संग्रहीत प्रक्रियाओं के साथ देखता हूं जिसमें तर्क की परतें होती हैं, या ऐसे परिदृश्यों में जहां बहुत सारे अनावश्यक कोड होते हैं। विस्तारित ईवेंट यहां एक स्वाभाविक पसंद है, क्योंकि इसका उपयोग आमतौर पर क्वेरी निष्पादन के बारे में जानकारी प्राप्त करने के लिए किया जाता है। घटनाओं के क्रम को समझने के लिए आप अक्सर session_id और टाइमस्टैम्प का उपयोग कर सकते हैं, लेकिन XE के लिए एक सत्र विकल्प है जो और भी अधिक विश्वसनीय है:ट्रैक कॉज़लिटी।
जब आप किसी सत्र के लिए ट्रैक कॉज़लिटी को सक्षम करते हैं, तो यह प्रत्येक ईवेंट में एक GUID और अनुक्रम संख्या जोड़ता है, जिसका उपयोग आप उस क्रम में कदम रखने के लिए कर सकते हैं जिसमें ईवेंट हुआ था। ओवरहेड न्यूनतम है, और यह कई समस्या निवारण परिदृश्यों में एक महत्वपूर्ण समय बचाने वाला हो सकता है।
सेट अप करें
WideWorldImporters डेटाबेस का उपयोग करके हम उपयोग करने के लिए एक संग्रहीत कार्यविधि तैयार करेंगे:
DROP PROCEDURE IF EXISTS [Sales].[usp_CustomerTransactionInfo]; GO CREATE PROCEDURE [Sales].[usp_CustomerTransactionInfo] @CustomerID INT AS SELECT [CustomerID], SUM([AmountExcludingTax]) FROM [Sales].[CustomerTransactions] WHERE [CustomerID] = @CustomerID GROUP BY [CustomerID]; SELECT COUNT([OrderID]) FROM [Sales].[Orders] WHERE [CustomerID] = @CustomerID GO
फिर हम एक इवेंट सेशन बनाएंगे:
CREATE EVENT SESSION [TrackQueries] ON SERVER ADD EVENT sqlserver.sp_statement_completed( WHERE ([sqlserver].[is_system]=(0))), ADD EVENT sqlserver.sql_statement_completed( WHERE ([sqlserver].[is_system]=(0))) ADD TARGET package0.event_file ( SET filename=N'C:\temp\TrackQueries',max_file_size=(256) ) WITH ( MAX_MEMORY = 4096 KB, EVENT_RETENTION_MODE = ALLOW_SINGLE_EVENT_LOSS, MAX_DISPATCH_LATENCY = 30 SECONDS, MAX_EVENT_SIZE = 0 KB, MEMORY_PARTITION_MODE = NONE, TRACK_CAUSALITY = ON, STARTUP_STATE = OFF );
हम तदर्थ प्रश्नों को भी चलाने जा रहे हैं, इसलिए हम दोनों sp_statement_completed (एक संग्रहीत कार्यविधि के भीतर पूर्ण किए गए कथन) और sql_statement_completed (पूर्ण किए गए कथन जो संग्रहीत कार्यविधि के भीतर नहीं हैं) दोनों को कैप्चर कर रहे हैं। ध्यान दें कि सत्र के लिए TRACK_CAUSALITY विकल्प चालू पर सेट है। फिर से, यह सेटिंग ईवेंट सत्र के लिए विशिष्ट है और इसे शुरू करने से पहले इसे सक्षम करना होगा। आप तुरंत सेटिंग को सक्षम नहीं कर सकते, जैसे आप सत्र के चलने के दौरान ईवेंट और लक्ष्यों को जोड़ या हटा सकते हैं।
UI के माध्यम से ईवेंट सत्र प्रारंभ करने के लिए, बस उस पर राइट-क्लिक करें और सत्र प्रारंभ करें चुनें.
परीक्षण
प्रबंधन स्टूडियो में हम निम्नलिखित कोड चलाएंगे:
EXEC [Sales].[usp_CustomerTransactionInfo] 490; SELECT [c].[CustomerID], [c].[CustomerName], [p].[FullName], [o].[OrderID] FROM [Application].[People] [p] JOIN [Sales].[Customers] [c] ON [p].[PersonID] = [c].[PrimaryContactPersonID] JOIN [Sales].[Orders] [o] ON [c].[CustomerID] = [o].[CustomerID] WHERE [p].[FullName] = 'Naseem Radan';
ये रहा हमारा एक्सई आउटपुट:
ध्यान दें कि निष्पादित की गई पहली क्वेरी, जिसे हाइलाइट किया गया है, SELECT @@SPID है, और इसमें FDCCB1CF-CA55-48AA-8FBA-7F5EBF870674 का GUID है। हमने इस क्वेरी को निष्पादित नहीं किया, यह पृष्ठभूमि में हुआ, और भले ही XE सत्र सिस्टम क्वेरीज़ को फ़िल्टर करने के लिए सेट किया गया हो, यह एक - किसी भी कारण से - अभी भी कैप्चर किया गया है।
अगली चार पंक्तियाँ उस कोड का प्रतिनिधित्व करती हैं जिसे हमने वास्तव में चलाया था। संग्रहीत कार्यविधि से दो प्रश्न हैं, संग्रहीत कार्यविधि स्वयं, और फिर हमारी तदर्थ क्वेरी। सभी में एक ही GUID, ACBFFD99-2400-4AFF-A33F-351821667B24 है। GUID के आगे अनुक्रम आईडी (seq) है, और प्रश्नों को एक से चार तक क्रमांकित किया जाता है।
हमारे उदाहरण में, हमने बयानों को अलग-अलग बैचों में अलग करने के लिए GO का उपयोग नहीं किया। ध्यान दें कि जब हम ऐसा करते हैं तो आउटपुट कैसे बदलता है:
EXEC [Sales].[usp_CustomerTransactionInfo] 490; GO SELECT [c].[CustomerID], [c].[CustomerName], [p].[FullName], [o].[OrderID] FROM [Application].[People] [p] JOIN [Sales].[Customers] [c] ON [p].[PersonID] = [c].[PrimaryContactPersonID] JOIN [Sales].[Orders] [o] ON [c].[CustomerID] = [o].[CustomerID] WHERE [p].[FullName] = 'Naseem Radan'; GO
हमारे पास अभी भी कुल पंक्तियों की संख्या समान है, लेकिन अब हमारे पास तीन GUID हैं। एक चयन @@ SPID (E8D136B8-092F-439D-84D6-D4EF794AE753) के लिए, एक संग्रहीत प्रक्रिया का प्रतिनिधित्व करने वाले तीन प्रश्नों के लिए (F962B9A4-0665-4802-9E6C-B217634D8787), और एक एड-हॉक क्वेरी के लिए (5DD6A5FE) -9702-4DE5-8467-8D7CF55B5D80)।
जब आप अपने एप्लिकेशन के डेटा को देख रहे होते हैं, तो आप सबसे अधिक यही देखेंगे, लेकिन यह इस बात पर निर्भर करता है कि एप्लिकेशन कैसे काम करता है। यदि यह कनेक्शन पूलिंग का उपयोग करता है, और कनेक्शन नियमित रूप से रीसेट किए जाते हैं (जो अपेक्षित है), तो प्रत्येक कनेक्शन का अपना GUID होगा।
आप नीचे दिए गए नमूना PowerShell कोड का उपयोग करके इसे फिर से बना सकते हैं:
while(1 -eq 1) { $SqlConn = New-Object System.Data.SqlClient.SqlConnection; $SqlConn.ConnectionString = "Data Source=Hedwig\SQL2017;Initial Catalog=WideWorldImporters;Integrated Security=True;Application Name = MyCoolApp"; $SQLConn.Open() $SqlCmd = $SqlConn.CreateCommand(); $SqlCmd.CommandText = "SELECT TOP 1 CustomerID FROM Sales.Customers ORDER BY NEWID();" $SqlCmd.CommandType = [System.Data.CommandType]::Text; $SqlReader = $SqlCmd.ExecuteReader(); $Results = New-Object System.Collections.ArrayList; while ($SqlReader.Read()) { $Results.Add($SqlReader.GetSqlInt32(0)) | Out-Null; } $SqlReader.Close(); $Value = Get-Random -InputObject $Results; $SqlCmd = $SqlConn.CreateCommand(); $SqlCmd.CommandText = "Sales.usp_CustomerTransactionInfo" $SqlCmd.CommandType = [System.Data.CommandType]::StoredProcedure; $SqlParameter = $SqlCmd.Parameters.AddWithValue("@CustomerID", $Value); $SqlParameter.SqlDbType = [System.Data.SqlDbType]::Int; $SqlCmd.ExecuteNonQuery(); $SqlConn.Close(); $Names = New-Object System.Collections.Generic.List``1[System.String] $SqlConn = New-Object System.Data.SqlClient.SqlConnection $SqlConn.ConnectionString = "Data Source=Hedwig\SQL2017;Initial Catalog=WideWorldImporters;User Id=aw_webuser;Password=12345;Application Name=AdventureWorks Online Ordering;Workstation ID=AWWEB01"; $SqlConn.Open(); $SqlCmd = $SqlConn.CreateCommand(); $SqlCmd.CommandText = "SELECT FullName FROM Application.People ORDER BY NEWID();"; $dr = $SqlCmd.ExecuteReader(); while($dr.Read()) { $Names.Add($dr.GetString(0)); } $SqlConn.Close(); $name = Get-Random -InputObject $Names; $query = [String]::Format("SELECT [c].[CustomerID], [c].[CustomerName], [p].[FullName], [o].[OrderID] FROM [Application].[People] [p] JOIN [Sales].[Customers] [c] ON [p].[PersonID] = [c].[PrimaryContactPersonID] JOIN [Sales].[Orders] [o] ON [c].[CustomerID] = [o].[CustomerID] WHERE [p].[FullName] = '{0}';", $name); $SqlConn = New-Object System.Data.SqlClient.SqlConnection $SqlConn.ConnectionString = "Data Source=Hedwig\SQL2017;Initial Catalog=WideWorldImporters;User Id=aw_webuser;Password=12345;Application Name=AdventureWorks Online Ordering;Workstation ID=AWWEB01"; $SqlConn.Open(); $SqlCmd = $sqlconnection.CreateCommand(); $SqlCmd.CommandText = $query $SqlCmd.ExecuteNonQuery(); $SqlConn.Close(); }
कोड को थोड़ा चलने देने के बाद विस्तारित ईवेंट आउटपुट का एक उदाहरण यहां दिया गया है:
हमारे पांच कथनों के लिए चार अलग-अलग GUID हैं, और यदि आप ऊपर दिए गए कोड को देखते हैं तो आप देखेंगे कि चार अलग-अलग कनेक्शन बने हैं। यदि आप rpc_completed ईवेंट को शामिल करने के लिए ईवेंट सत्र को बदलते हैं, तो आप sp_reset_connection निष्पादन के साथ प्रविष्टियाँ देख सकते हैं।
आपका XE आउटपुट आपके कोड और आपके एप्लिकेशन पर निर्भर करेगा; मैंने शुरू में उल्लेख किया था कि अधिक जटिल संग्रहीत प्रक्रियाओं का निवारण करते समय यह उपयोगी था। निम्नलिखित उदाहरण पर विचार करें:
DROP PROCEDURE IF EXISTS [Sales].[usp_CustomerTransactionInfo]; GO CREATE PROCEDURE [Sales].[usp_CustomerTransactionInfo] @CustomerID INT AS SELECT [CustomerID], SUM([AmountExcludingTax]) FROM [Sales].[CustomerTransactions] WHERE [CustomerID] = @CustomerID GROUP BY [CustomerID]; SELECT COUNT([OrderID]) FROM [Sales].[Orders] WHERE [CustomerID] = @CustomerID GO DROP PROCEDURE IF EXISTS [Sales].[usp_GetFullCustomerInfo]; GO CREATE PROCEDURE [Sales].[usp_GetFullCustomerInfo] @CustomerID INT AS SELECT [o].[CustomerID], [o].[OrderDate], [ol].[StockItemID], [ol].[Quantity], [ol].[UnitPrice] FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID] WHERE [o].[CustomerID] = @CustomerID ORDER BY [o].[OrderDate] DESC; SELECT [o].[CustomerID], SUM([ol].[Quantity]*[ol].[UnitPrice]) FROM [Sales].[Orders] [o] JOIN [Sales].[OrderLines] [ol] ON [o].[OrderID] = [ol].[OrderID] WHERE [o].[CustomerID] = @CustomerID GROUP BY [o].[CustomerID] ORDER BY [o].[CustomerID] ASC; GO DROP PROCEDURE IF EXISTS [Sales].[usp_GetCustomerData]; GO CREATE PROCEDURE [Sales].[usp_GetCustomerData] @CustomerID INT AS BEGIN SELECT * FROM [Sales].[Customers] EXEC [Sales].[usp_CustomerTransactionInfo] @CustomerID EXEC [Sales].[usp_GetFullCustomerInfo] @CustomerID END GO
यहां हमारे पास दो संग्रहीत कार्यविधियाँ हैं, usp_TransctionInfo और usp_GetFullCustomerInfo, जिन्हें किसी अन्य संग्रहीत कार्यविधि, usp_GetCustomerData द्वारा बुलाया जाता है। यह देखने के लिए असामान्य नहीं है, या संग्रहीत प्रक्रियाओं के साथ घोंसले के अतिरिक्त स्तर भी देखें। यदि हम प्रबंधन स्टूडियो से usp_GetCustomerData निष्पादित करते हैं, तो हम निम्नलिखित देखते हैं:
EXEC [Sales].[usp_GetCustomerData] 981;
यहां, सभी निष्पादन एक ही GUID, BF54CD8F-08AF-4694-A718-D0C47DBB9593 पर हुए, और हम seq कॉलम का उपयोग करके एक से आठ तक क्वेरी निष्पादन का क्रम देख सकते हैं। ऐसे मामलों में जहां कई संग्रहित प्रक्रियाएं होती हैं, अनुक्रम आईडी मान सैकड़ों या हजारों में आने के लिए असामान्य नहीं है।
अंत में, उस मामले में जहां आप क्वेरी निष्पादन को देख रहे हैं और आपने ट्रैक कॉज़लिटी को शामिल किया है, और आपको एक क्वेरी मिलती है जो खराब प्रदर्शन कर रही है - क्योंकि आपने आउटपुट को अवधि या किसी अन्य मीट्रिक के आधार पर सॉर्ट किया है, ध्यान दें कि आप अन्य पा सकते हैं GUID पर समूहबद्ध करके प्रश्न:
आउटपुट को अवधि (लाल रंग में परिचालित उच्चतम मूल्य) के अनुसार क्रमबद्ध किया गया है, और मैंने टॉगल बुकमार्क बटन का उपयोग करके इसे (बैंगनी रंग में) बुकमार्क किया है। यदि हम GUID के लिए अन्य प्रश्नों को देखना चाहते हैं, GUID द्वारा समूह (शीर्ष पर कॉलम नाम पर राइट-क्लिक करें, फिर इस कॉलम द्वारा समूह का चयन करें), और फिर हमारी क्वेरी पर वापस जाने के लिए अगला बुकमार्क बटन का उपयोग करें:पी>
अब हम उन सभी प्रश्नों को देख सकते हैं जो एक ही कनेक्शन में और निष्पादित क्रम में निष्पादित होते हैं।
निष्कर्ष
क्वेरी प्रदर्शन की समस्या निवारण और SQL सर्वर के भीतर ईवेंट के क्रम को समझने का प्रयास करते समय ट्रैक कॉज़लिटी विकल्प अत्यधिक उपयोगी हो सकता है। यह सुनिश्चित करने में सहायता के लिए कि आप सही फ़ील्ड/क्रिया से मेल खा रहे हैं और उचित बेजोड़ ईवेंट ढूंढ रहे हैं, जोड़ी_मैचिंग लक्ष्य का उपयोग करने वाला ईवेंट सत्र सेट करते समय भी यह उपयोगी होता है। फिर से, यह एक सत्र स्तरीय सेटिंग है, इसलिए इसे ईवेंट सत्र शुरू करने से पहले लागू करना होगा। चल रहे सत्र के लिए, ईवेंट सत्र को रोकें, विकल्प को सक्षम करें, फिर इसे फिर से प्रारंभ करें।