Die Antworten von @Kin, @AaronBertrand und @DBAFromTheCold sind großartig und sehr hilfreich. Eine wichtige Information, die ich während des Testens herausgefunden habe, dass die anderen Antworten ausgelassen wurden, ist, dass Sie den Index verwenden müssen, der von sys.partitions
dem angegebenen Index zurückgegeben wird , HOBT_ID
wenn Sie nachschlagen %%lockres%%
(über einen Index-Abfrage-Hinweis). Dieser Index ist nicht immer der PK- oder Clustered-Index.
Beispielsweise:
--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
;
Hier ist ein Beispielskript, das mit Stücken aus jeder dieser Antworten modifiziert wurde.
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;
Sie haben die hobt_id, sodass die folgende Abfrage die Tabelle identifiziert:
Anschließend können Sie die folgende Anweisung ausführen, um die Zeile in der Tabelle zu identifizieren (falls sie noch vorhanden ist):
Seien Sie vorsichtig mit der obigen Anweisung, sie scannt jedoch die Zieltabelle. Führen Sie also READ UNCOMMITTED aus und überwachen Sie Ihren Server.
Hier ist ein Artikel von Grant Fritchey über %% LOCKRES %% - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/
Und hier ist ein Artikel aus meinem eigenen Blog über die Verwendung von %% LOCKRES %% zum Identifizieren von Zeilen aus einem erweiterten Ereignis: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/
quelle
Dies ist eine Ergänzung zu den bereits von DBAFromTheCold und Aaron Bertrand geposteten Antworten .
Microsoft hat noch
%%lockres%%
als undokumentierte Funktion verlassen .Unten finden Sie das Skript, das Ihnen helfen wird :
Lesen Sie auch diesen hervorragenden Blogeintrag über: Der seltsame Fall des zweifelhaften Deadlocks und die nicht so logische Sperre
quelle
Entschuldigung, habe bereits an dieser Antwort gearbeitet und wollte etwas posten, als die andere erschien. Hinzufügen als Community-Wiki nur, weil es ein etwas anderer Ansatz ist und ein bisschen andere Informationen hinzufügt.
Das
543066506c7c
ist im Wesentlichen ein Hash des Primärschlüssels, und Sie können diese Zeile (und möglicherweise alle Zeilen mit einer Hash-Kollision) mit dieser dynamischen SQL abrufen:Sie können dies natürlich ohne dynamisches SQL tun, aber dies gibt Ihnen eine schöne Vorlage für ein Snippet oder eine gespeicherte Prozedur, in die Sie einfach die Werte einfügen können, wenn dies etwas ist, das Sie häufig beheben. (Sie können auch den Tabellennamen parametrisieren und das Parsen der Zeichenfolge KEY: einbauen, um alles für Sie dynamisch zu bestimmen, aber ich dachte, dass dies für diesen Beitrag möglicherweise nicht möglich ist.)
quelle