Làm cách nào tôi có thể chuyển đổi một khóa trong báo cáo khóa chết của SQL Server thành giá trị?


15

Tôi có một báo cáo bế tắc cho tôi biết rằng đã có xung đột liên quan đến Waitresource = "KEY: 9: 72057632651542528 (543066506c7c)" và tôi có thể thấy điều này:

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

trong <danh sách tài nguyên>. Tôi muốn có thể tìm giá trị thực cho khóa (ví dụ: id = 12345). Câu lệnh SQL nào tôi cần sử dụng để có được thông tin đó?

Câu trả lời:


9

Câu trả lời từ @Kin, @AaronBertrand và @DBAFromTheCold rất hay và rất hữu ích. Một phần quan trọng của thông tin tôi tìm thấy trong thử nghiệm rằng câu trả lời khác bỏ ra là bạn cần phải sử dụng các chỉ số được trả về từ sys.partitionscho được HOBT_IDkhi nhìn lên %%lockres%%(thông qua một gợi ý truy vấn index). Chỉ số này không phải luôn luôn là chỉ số PK hoặc cụm.

Ví dụ:

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

Dưới đây là một kịch bản ví dụ được sửa đổi bằng cách sử dụng các phần từ mỗi câu trả lời này.

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;

Tự động xác định tên cơ sở dữ liệu là một giá trị gia tăng tốt ở đây, cùng với gợi ý chỉ mục. Cảm ơn!
Đánh dấu Freeman

14

Bạn có hobt_id nên truy vấn sau sẽ xác định bảng: -

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

Từ đó bạn có thể chạy câu lệnh sau để xác định hàng trong bảng (nếu nó vẫn tồn tại): -

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

Tuy nhiên, hãy cẩn thận với tuyên bố trên, nó sẽ quét bảng mục tiêu để chạy trong READ UNCOMMITTED và theo dõi máy chủ của bạn.

Đây là một bài viết của Grant Fritchey về %% LOCKRES %% - http://www.scarydba.com/2010/03/18/undocumented-virtual-column-lockres/

Và đây là một bài viết từ blog của riêng tôi về việc sử dụng %% LOCKRES %% để xác định các hàng từ một sự kiện mở rộng: - https://dbafromthecold.wordpress.com/2015/02/24/identifying-blocking-via-extends-events/


Cảm ơn bạn đã phản hồi nhanh chóng và bao gồm các liên kết đến các bài đăng blog hữu ích.
Đánh dấu Freeman

9

Đây là phần bổ sung cho các câu trả lời đã được đăng bởi DBAFromTheColdAaron Bertrand .

Microsoft vẫn để lại %%lockres%%là tính năng không có giấy tờ .

Dưới đây là kịch bản sẽ giúp bạn :

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

Cũng tham khảo bài đăng trên blog tuyệt vời này về: Trường hợp tò mò về sự bế tắc đáng ngờ và Khóa không hợp lý


Tôi đang chọn đây là câu trả lời. Mặc dù các giải pháp được cung cấp bởi DBAFromTheCold và Aaron Bertrand đều hoạt động, nhưng điều này cho phép tôi có được thông tin bằng cách chỉ cung cấp KEY, giúp tôi làm việc này hiệu quả hơn (mặc dù tại một số tải bổ sung trên cơ sở dữ liệu để có được thông tin mà tôi đã có, nhưng sẽ thay vì không cùng nhau để cung cấp).
Đánh dấu Freeman

Kin, tôi nghĩ rằng bạn đã đi một chặng đường dài ở đây, và tôi ngày càng ấn tượng hơn với câu trả lời của bạn mọi lúc. Tuy nhiên, bạn nên tiết lộ (các) nguồn của mình khi bạn đề xuất mã được viết bởi người khác (mã giống hệt ở đây , ở đâyở đây ).
Aaron Bertrand

@AaronBertrand Tôi đã có mã này trong một thời gian dài và tôi không có bất kỳ tài liệu tham khảo nào, vì tôi đã sử dụng nó. Cảm ơn bạn vì bạn đã chỉ ra tài liệu tham khảo (tôi cũng sẽ thêm nó vào kho lưu trữ của tôi). Ngoài ra, cảm ơn những lời tốt đẹp! Tôi phải đi một chặng đường dài để học hỏi và trả lại cho cộng đồng. Lời xin lỗi và tôi thực sự không có nghĩa là không trích dẫn tài liệu tham khảo .
Kin Shah

6

Xin lỗi, đã làm việc với câu trả lời này và sắp đăng khi cái kia xuất hiện. Thêm dưới dạng wiki cộng đồng chỉ vì đó là một cách tiếp cận hơi khác và thêm một chút thông tin khác.

Về 543066506c7ccơ bản, đó là hàm băm của khóa chính và bạn có thể truy xuất hàng đó (và có khả năng là bất kỳ hàng nào có xung đột băm) bằng cách sử dụng SQL động này:

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

Tất nhiên, bạn có thể làm điều này mà không cần SQL động, nhưng điều này cung cấp cho bạn một khuôn mẫu đẹp cho đoạn mã hoặc thủ tục được lưu trữ trong đó bạn chỉ có thể cắm các giá trị vào, nếu đây là điều bạn khắc phục được rất nhiều. (Bạn cũng có thể tham số hóa tên bảng và bạn cũng có thể xây dựng phân tích cú pháp của chuỗi KEY: để xác định mọi thứ cho bạn một cách linh hoạt, nhưng tôi nghĩ rằng điều đó có thể nằm ngoài phạm vi của bài đăng này.)

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.