SQL Server kilitlenme raporundaki bir anahtarı değere nasıl dönüştürebilirim?


15

Bana waitresource = "KEY: 9: 72057632651542528 (543066506c7c)" içeren bir çakışma olduğunu söyleyen bir kilitlenme raporu var ve bunu görebilirsiniz:

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

<kaynak-listesi> içinde. Anahtar için gerçek değeri bulmak istiyorum (örneğin id = 12345). Bu bilgileri elde etmek için hangi SQL deyimini kullanmam gerekir?

Yanıtlar:


9

@Kin, @AaronBertrand ve @DBAFromTheCold'dan gelen cevaplar harika ve çok yardımcı oldular. Test sırasında bulduğum önemli bir bilgi parçası, diğer cevapların dışarıda bırakıldığını (bir dizin sorgu ipucu yoluyla) ararken sys.partitionsverilen için verilen dizini kullanmanız gerektiğidir . Bu dizin her zaman PK veya kümelenmiş dizin değildir.HOBT_ID%%lockres%%

Örneğin:

--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
;

İşte bu cevapların her birinden parçalar kullanılarak değiştirilmiş bir örnek betik.

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;

Veritabanı adını otomatik olarak belirlemek, burada dizin ipucu ile birlikte güzel bir katma değerdir. Teşekkürler!
Mark Freeman

14

Aşağıdaki sorgu tabloyu tanımlamak için hobt_id var: -

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

Bundan sonra, tablodaki satırı tanımlamak için aşağıdaki ifadeyi çalıştırabilirsiniz (hala varsa): -

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

Ancak yukarıdaki ifadeye dikkat edin, hedef tabloyu tarar, bu yüzden OKULMADIYI OKUYUN ve sunucunuzu izleyin.

Grant Fritchey'nin %% LOCKRES %% hakkında bir makalesi - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/

Ve işte kendi blogumdan, genişletilmiş bir etkinliğin satırlarını tanımlamak için %% LOCKRES %% kullanma hakkında bir makale: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extended-events/


Hızlı yanıt ve faydalı blog yayınlarına bağlantılar eklediğiniz için teşekkür ederiz.
Mark Freeman

9

DBAFromTheCold ve Aaron Bertrand tarafından gönderilen cevaplara bir ektir .

Microsoft hala %%lockres%%belgesiz özellik olarak kaldı .

Aşağıda size yardımcı olacak komut dosyası verilmiştir :

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

Ayrıca şu mükemmel blog gönderisine bakın: Şüpheli Deadlock'un Tuhaf Hikayesi ve Mantıksız Kilit


Bunu cevap olarak seçiyorum. Her ne kadar DBAFromTheCold ve Aaron Bertrand tarafından sağlanan çözümler işe yarıyor olsa da, bu sadece KEY'yi sağlayarak bilgiyi almama izin veriyor, bu da benim için daha verimli hale getiriyor (veritabanımdaki bazı ek yüklerde zaten sahip olduğum bilgileri almakla birlikte, yerine sağlamak için bir parça değil).
Mark Freeman

Kin, sanırım buraya çok yol kattın ve her zaman cevaplarından daha fazla etkilendim. Ancak, başkası tarafından yazılan kodu ( burada , burada ve burada aynı kod) önerdiğinizde kaynaklarınızı göstermelisiniz .
Aaron Bertrand

@AaronBertrand Uzun zamandır bu kodu vardı ve ben kullandım beri herhangi bir referans yoktu. Referansı belirttiğiniz için teşekkür ederim (veri havuzuma da ekleyeceğim). Ayrıca, nazik sözler için teşekkürler! Öğrenmek ve topluma geri vermek için uzun bir yol kat etmeliyim. Özür dilerim ve ben gerçekten referans göstermemek istememiştik .
Kin Shah

6

Üzgünüz, bu cevap üzerinde zaten çalışıyordum ve diğeri ortaya çıktığında yayınlamak üzereydim. Topluluk wiki'si olarak eklemek sadece biraz farklı bir yaklaşım ve biraz başka bilgi eklediğinden.

543066506c7cEsasen birincil anahtarın bir karma olduğunu ve bu dinamik SQL kullanarak o satır almak (ve bir karma çarpışma ile potansiyel olarak herhangi bir satır) olabilir:

-- 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;

Bunu elbette dinamik SQL olmadan yapabilirsiniz, ancak bu, çok fazla sorun giderdiğiniz bir şeyse, sadece değerleri ekleyebileceğiniz bir snippet veya saklı yordam için güzel bir şablon sağlar. (Ayrıca tablo adını parametreleştirebilir ve dinamik olarak sizin için her şeyi belirlemek için KEY: string ayrıştırma inşa edebilirsiniz, ancak bu yazı için kapsam dışında olabilir düşündüm.)

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.