Odpowiedzi @Kin, @AaronBertrand i @DBAFromTheCold są świetne i bardzo pomocne. Jedną ważną informacją, którą znalazłem podczas testowania, że pozostałe odpowiedzi zostały pominięte, jest to, że musisz użyć indeksu, który jest zwracany sys.partitions
dla podanej HOBT_ID
podczas wyszukiwania %%lockres%%
(za pomocą podpowiedzi do indeksu). Ten indeks nie zawsze jest PK lub indeksem klastrowym.
Na przykład:
--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
;
Oto przykładowy skrypt zmodyfikowany przy użyciu fragmentów z każdej z tych odpowiedzi.
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;
Masz hobt_id, więc następujące zapytanie zidentyfikuje tabelę: -
Następnie możesz uruchomić następującą instrukcję, aby zidentyfikować wiersz w tabeli (jeśli nadal istnieje): -
Uważaj na powyższe stwierdzenie, jednak skanuje ono tabelę docelową, więc uruchom w CZYTANIE NIEZGODNE i monitoruj serwer.
Oto artykuł Granta Fritchey'a na temat %% LOCKRES %% - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/
A oto artykuł z mojego własnego bloga na temat używania %% LOCKRES %% do identyfikowania wierszy z rozszerzonego zdarzenia: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/
źródło
Jest to uzupełnienie odpowiedzi już opublikowanych przez DBAFromTheCold i Aaron Bertrand .
Microsoft wciąż pozostawił
%%lockres%%
funkcję nieudokumentowaną .Poniżej znajduje się skrypt, który pomoże ci :
Zapoznaj się także z doskonałym postem na blogu: Ciekawy przypadek zakłopotanego impasu i niezbyt logiczna blokada
źródło
Niestety, pracowałem już nad tą odpowiedzią i zamierzałem opublikować, gdy pojawi się druga. Dodanie jako wiki społeczności tylko dlatego, że jest nieco inne podejście i dodaje trochę innych informacji.
Zasadniczo
543066506c7c
jest to skrót klucza podstawowego i możesz pobrać ten wiersz (i potencjalnie dowolne wiersze z kolizją skrótu) za pomocą tego dynamicznego SQL:Oczywiście możesz to zrobić bez dynamicznego SQL, ale daje to fajny szablon dla fragmentu lub procedury składowanej, w której możesz po prostu podłączyć wartości, jeśli jest to coś, co często rozwiązujesz. (Można również sparametryzować nazwę tabeli, a także można parsować ciąg znaków KEY:, aby określić wszystko dynamicznie, ale pomyślałem, że może to być poza zakresem tego postu.)
źródło