मैं SQL Server डेडलॉक रिपोर्ट में एक कुंजी को मूल्य में कैसे बदल सकता हूं?


15

मेरे पास एक डेडलॉक रिपोर्ट है जो मुझे बताती है कि वेट्रेस सोर्स = "KEY: 9: 72057632651542528 (543066506c7c)" को लेकर एक संघर्ष हुआ था और मैं इसे देख सकता हूं:

<keylock hobtid="72057632651542528" dbid="9" objectname="MyDatabase.MySchema.MyTable" indexname="MyPrimaryKeyIndex" id="locka8c6f4100" mode="X" associatedObjectId="72057632651542528">

<संसाधन-सूची> के भीतर। मैं कुंजी (आईडी = 12345, उदाहरण के लिए) के लिए वास्तविक मूल्य खोजने में सक्षम होना चाहता हूं। उस जानकारी को प्राप्त करने के लिए मुझे किस एसक्यूएल स्टेटमेंट का उपयोग करना होगा?

जवाबों:


9

@Kin, @AaronBertrand, और @DBAFromTheCold के उत्तर बहुत अच्छे हैं और बहुत मददगार थे। जानकारी का एक महत्वपूर्ण टुकड़ा मैंने परीक्षण के दौरान पाया कि अन्य उत्तर बचे हुए हैं, आपको उस सूचकांक का उपयोग करने की आवश्यकता है sys.partitionsजिसे देखते HOBT_IDसमय %%lockres%%(इंडेक्स क्वेरी हिंट के माध्यम से) दिए गए के लिए दिया गया है । यह सूचकांक हमेशा PK या क्लस्टर इंडेक्स नहीं होता है।

उदाहरण के लिए:

--Sometimes this does not return the correct results.
SELECT lockResKey = %%lockres%% ,* 
FROM [MyDB].[dbo].[myTable]  
WHERE %%lockres%% = @lockres
;
--But if you add the index query hint, it does return the correct results
SELECT lockResKey = %%lockres%% ,* 
FROM [MyDB].[dbo].[myTable] WITH(NOLOCK INDEX([IX_MyTable_NonClustered_index]))  
WHERE %%lockres%% = @lockres
;

इनमें से प्रत्येक उत्तर से टुकड़ों का उपयोग करके एक उदाहरण लिपि को संशोधित किया गया है।

declare @keyValue varchar(256);
SET @keyValue = 'KEY: 5:72057598157127680 (92d211c2a131)' --Output from deadlock graph: process-list/process[waitresource] -- CHANGE HERE !
------------------------------------------------------------------------
--Should not have to change anything below this line: 
declare @lockres nvarchar(255), @hobbitID bigint, @dbid int, @databaseName sysname;
--.............................................
--PARSE @keyValue parts:
SELECT @dbid = LTRIM(SUBSTRING(@keyValue, CHARINDEX(':', @keyValue) + 1, CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) - (CHARINDEX(':', @keyValue) + 1) ));
SELECT @hobbitID = convert(bigint, RTRIM(SUBSTRING(@keyValue, CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) + 1, CHARINDEX('(', @keyValue) - CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) - 1)));
SELECT @lockRes = RTRIM(SUBSTRING(@keyValue, CHARINDEX('(', @keyValue) + 0, CHARINDEX(')', @keyValue) - CHARINDEX('(', @keyValue) + 1));
--.............................................
--Validate DB name prior to running dynamic SQL
SELECT @databaseName = db_name(@dbid);  
IF not exists(select * from sys.databases d where d.name = @databaseName)
BEGIN
    RAISERROR(N'Database %s was not found.', 16, 1, @databaseName);
    RETURN;
END

declare @objectName sysname, @indexName sysname, @schemaName sysname;
declare @ObjectLookupSQL as nvarchar(max) = '
SELECT @objectName = o.name, @indexName = i.name, @schemaName = OBJECT_SCHEMA_NAME(p.object_id, @dbid)
FROM ' + quotename(@databaseName) + '.sys.partitions p
JOIN ' + quotename(@databaseName) + '.sys.indexes i ON p.index_id = i.index_id AND p.[object_id] = i.[object_id]
JOIN ' + quotename(@databaseName)+ '.sys.objects o on o.object_id = i.object_id
WHERE hobt_id = @hobbitID'
;
--print @ObjectLookupSQL
--Get object and index names
exec sp_executesql @ObjectLookupSQL
    ,N'@dbid int, @hobbitID bigint, @objectName sysname OUTPUT, @indexName sysname OUTPUT, @schemaName sysname OUTPUT'
    ,@dbid = @dbid
    ,@hobbitID = @hobbitID
    ,@objectName = @objectName output
    ,@indexName = @indexName output
    ,@schemaName = @schemaName output
;

DECLARE @fullObjectName nvarchar(512) = quotename(@databaseName) + '.' + quotename(@schemaName) + '.' + quotename(@objectName);
SELECT fullObjectName = @fullObjectName, lockIndex = @indexName, lockRes_key = @lockres, hobt_id = @hobbitID, waitresource_keyValue = @keyValue;

--Validate object name prior to running dynamic SQL
IF OBJECT_iD( @fullObjectName) IS NULL 
BEGIN
    RAISERROR(N'The object "%s" was not found.',16,1,@fullObjectName);
    RETURN;
END

--Get the row that was blocked
--NOTE: we use the NOLOCK hint to avoid locking the table when searching by %%lockres%%, which might generate table scans.
DECLARE @finalResult nvarchar(max) = N'SELECT lockResKey = %%lockres%% ,* 
FROM ' + @fullObjectName
+ ISNULL(' WITH(NOLOCK INDEX(' + QUOTENAME(@indexName) + ')) ', '')  
+ ' WHERE %%lockres%% = @lockres'
;

--print @finalresult
EXEC sp_executesql @finalResult, N'@lockres nvarchar(255)', @lockres = @lockres;

इंडेक्स हिंटिंग के साथ-साथ डेटाबेस नाम को स्वचालित रूप से निर्धारित करना एक अच्छा मूल्य है। धन्यवाद!
मार्क फ्रीमैन

14

आपके पास hobt_id है इसलिए निम्न क्वेरी तालिका की पहचान करेगी: -

SELECT o.name
FROM sys.partitions p
INNER JOIN sys.objects o ON p.object_id = o.object_id
WHERE p.hobt_id = 72057632651542528

उसके बाद आप तालिका में पंक्ति को पहचानने के लिए निम्न कथन चला सकते हैं (यदि यह अभी भी मौजूद है): -

SELECT %%LOCKRES%%,  *
FROM [TABLE NAME] WITH(INDEX(MyPrimaryKeyIndex))
WHERE %%LOCKRES%% = '(543066506c7c)'

हालांकि, उपरोक्त कथन से सावधान रहें, यह लक्ष्य तालिका को स्कैन करेगा ताकि READ UNCOMMITTED में चला जाए और अपने सर्वर की निगरानी करें।

यहाँ% Fr लोके%% के बारे में ग्रांट फ्रिट्ची का एक लेख दिया गया है - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/

और यहाँ पर अपने ब्लॉग से %% LOCKRES %% का उपयोग करके एक विस्तृत घटना से पंक्तियों की पहचान करने के बारे में एक लेख है: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/


त्वरित प्रतिक्रिया के लिए धन्यवाद, और सहायक ब्लॉग पोस्ट के लिंक को शामिल करने के लिए।
मार्क फ्रीमैन

9

DBAFromTheCold और हारून बर्ट्रेंड द्वारा पहले से ही पोस्ट किए गए उत्तरों का पूरक है ।

Microsoft अभी भी %%lockres%%अनिर्दिष्ट सुविधा के रूप में बचा है ।

नीचे स्क्रिप्ट है जो आपकी मदद करेगी :

declare @databaseName varchar(100) = 'yourdatabaseName' --CHANGE HERE !
declare @keyValue varchar(100) = 'KEY: 9:72057632651542528 (543066506c7c)' --Output from deadlock graph -- CHANGE HERE !
declare @lockres varchar(100)
declare @hobbitID bigint

select @hobbitID = convert(bigint, RTRIM(SUBSTRING(@keyValue, CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) + 1, CHARINDEX('(', @keyValue) - CHARINDEX(':', @keyValue, CHARINDEX(':', @keyValue) + 1) - 1)))

select @lockRes = RTRIM(SUBSTRING(@keyValue, CHARINDEX('(', @keyValue) + 1, CHARINDEX(')', @keyValue) - CHARINDEX('(', @keyValue) - 1))

declare @objectName sysname
declare @ObjectLookupSQL as nvarchar(max) = '
SELECT @objectName = o.name
FROM ' + quotename(@databaseName) + '.sys.partitions p
JOIN ' + quotename(@databaseName) + '.sys.indexes i ON p.index_id = i.index_id AND p.[object_id] = i.[object_id]
join ' + quotename(@databaseName)+ '.sys.objects o on o.object_id = i.object_id
WHERE hobt_id = ' + convert(nvarchar(50), @hobbitID) + ''

--print @ObjectLookupSQL
exec sp_executesql @ObjectLookupSQL
    ,N'@objectName sysname OUTPUT'
    ,@objectName = @objectName output

--print @objectName

declare @finalResult nvarchar(max) = N'select %%lockres%% ,* 
from ' + quotename(@databaseName) + '.dbo.' + @objectName + '
where %%lockres%% = ''(' + @lockRes + ')''
'
--print @finalresult
exec sp_executesql @finalResult

इस उत्कृष्ट ब्लॉग पोस्ट को भी देखें: द क्यूरियस केस ऑफ डबियस डेडलॉक एंड द नॉट सो लॉजिकल लॉक


मैं इसका जवाब के रूप में चुन रहा हूं। यद्यपि DBAFromTheCold और आरोन बर्ट्रेंड द्वारा प्रदान किए गए समाधान दोनों काम करते हैं, यह मुझे केवल कुंजी प्रदान करके जानकारी प्राप्त करने की अनुमति देता है, जिससे मेरे लिए यह अधिक कुशल हो जाता है (हालांकि डेटाबेस पर कुछ अतिरिक्त लोड पर जानकारी प्राप्त करने के लिए जो मेरे पास पहले से है, लेकिन बल्कि प्रदान करने के लिए एक साथ टुकड़ा नहीं)।
मार्क फ्रीमैन

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

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

6

क्षमा करें, पहले से ही इस जवाब पर काम कर रहा था और दूसरे के दिखाई देने पर पोस्ट करने के बारे में। केवल समुदाय विकी के रूप में जोड़ना क्योंकि यह थोड़ा अलग दृष्टिकोण है और अन्य जानकारी को जोड़ता है।

543066506c7cअनिवार्य रूप से प्राथमिक कुंजी के हैश है, और आप (और एक हैश टकराव के साथ संभवतः किसी भी पंक्तियाँ) उस पंक्ति प्राप्त कर सकते हैं इस गतिशील एसक्यूएल का उपयोग कर:

-- Given: KEY: 9:72057632651542528 (543066506c7c)
-- and object = MyDatabase.MySchema.MyTable

DECLARE 
  @hobt BIGINT = 72057632651542528,
  @db SYSNAME = DB_NAME(9),
  @res VARCHAR(255) = '(543066506c7c)';

DECLARE @exec NVARCHAR(MAX) = QUOTENAME(@db) + N'.sys.sp_executesql';

DECLARE @sql NVARCHAR(MAX) = N'SELECT %%LOCKRES%%,*
  FROM MySchema.MyTable WHERE %%LOCKRES%% = @res;';

EXEC @exec @sql, N'@res VARCHAR(255)', @res;

आप इसे गतिशील SQL के बिना कर सकते हैं, बेशक, लेकिन यह आपको एक स्निपेट या संग्रहीत प्रक्रिया के लिए एक अच्छा टेम्पलेट देता है जहां आप बस मूल्यों को प्लग कर सकते हैं, अगर यह कुछ ऐसा है जो आपको बहुत परेशान करता है। (आप तालिका के नाम को भी मानकीकृत कर सकते हैं, और आप गतिशील रूप से आपके लिए सब कुछ निर्धारित करने के लिए कुंजी: स्ट्रिंग की पार्सिंग में भी निर्माण कर सकते हैं, लेकिन मुझे लगा कि इस पद के लिए गुंजाइश से बाहर हो सकता है।)

हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.