Les réponses de @Kin, @AaronBertrand et @DBAFromTheCold sont excellentes et ont été très utiles. Une information importante que j'ai trouvée lors des tests que les autres réponses ont omis est que vous devez utiliser l'index qui est renvoyé sys.partitions
pour le donné HOBT_ID
lorsque vous recherchez le %%lockres%%
(via un indice de requête d'index). Cet index n'est pas toujours l'index PK ou cluster.
Par exemple:
--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
;
Voici un exemple de script modifié en utilisant des morceaux de chacune de ces réponses.
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;
Vous avez le hobt_id donc la requête suivante identifiera la table: -
À partir de là, vous pouvez ensuite exécuter l'instruction suivante pour identifier la ligne du tableau (si elle existe toujours): -
Soyez prudent avec la déclaration ci-dessus, cependant, elle analysera la table cible, exécutez-la dans LIRE NON COMMIS et surveillez votre serveur.
Voici un article de Grant Fritchey sur %% LOCKRES %% - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/
Et voici un article de mon propre blog sur l'utilisation de %% LOCKRES %% pour identifier les lignes d'un événement étendu: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/
la source
Le est un complément aux réponses déjà publiées par DBAFromTheCold et Aaron Bertrand .
Microsoft est toujours laissé
%%lockres%%
comme fonctionnalité non documentée .Voici le script qui vous aidera :
Reportez-vous également à cet excellent article de blog sur: Le cas curieux du blocage douteux et du verrou pas si logique
la source
Désolé, je travaillais déjà sur cette réponse et sur le point de poster lorsque l'autre est apparu. Ajouter en tant que wiki communautaire uniquement parce que c'est une approche légèrement différente et ajoute un peu d'autres informations.
Le
543066506c7c
est essentiellement un hachage de la clé primaire, et vous pouvez récupérer cette ligne (et potentiellement toutes les lignes avec une collision de hachage) à l'aide de ce SQL dynamique:Vous pouvez le faire sans SQL dynamique, bien sûr, mais cela vous donne un joli modèle pour un extrait ou une procédure stockée où vous pouvez simplement brancher les valeurs, si c'est quelque chose que vous dépannez beaucoup. (Vous pouvez également paramétrer le nom de la table, et vous pouvez également créer une analyse syntaxique de la chaîne KEY: pour tout déterminer dynamiquement pour vous, mais j'ai pensé que cela pourrait être hors de portée pour ce post.)
la source