Welcher Admin kennt das nicht: Die Anwender schreien, dass die Anwendung zu langsam ist? Was tun? Erstmal einen Blick auf die aktuellen Prozesse werfen. Welche Prozesse haben den Status
"suspended"?
SELECT *
FROM master..sysprocesses p
WHERE p.status = 'suspended'
Aber was soll diese Wait-Resource-Spalte mit der kryptischen Zeichenfolge? Diese Frage habe ich mir auch schon gestellt und aus diesem Grund eine Prozedur erstellt, die aus der kryptischen
Zeichenfolge weitgehend den genauen Teil, der blockiert wird, spezifiziert.
Es gibt verschiedene Arten von waitresource-Strings. Ein wichtiges Kriterium für die Erkennung des Objektes, auf den gewartet wird, ist der vorangestellte Objekttyp. Dieser Typ kann
folgende Ausprägungen haben:
RID: In diesem Fall liegt die Sperre auf der internen ROWID einer Tabelle. Eine ROWID existiert nur auf Tabellen, die keinen eindeutigen Clustered Index haben. Es ist
eine vom SQL Server erzeugte GUID zur eindeutigen Identifikation des Datensatzes.
PAGE / PAG: Im Falle einer Page als Wartemedium handelt es sich um eine dedizierte 8-KB-Seite im SQL Server. Das können verschiedene Arten von Pages sein. Meistens handelt es
sich um Index-Pages und Table-Pages.
EXT: EXT steht für Extent, welcher physikalisch mit einer Page vergleichbar ist, aber aus einer Menge von 8 Pages = 64 KB besteht.
OBJECT / TAB: In diesem Fall wird auf das vollständige Objekt, meistens eine Tabelle, gewartet, das heißt, der Prozess möchte das Objekt vollständig sperren.
KEY: Wenn eine Tabelle kein Heap ist, sondern einen clustered Index hat, so wird analog zur RowID auf Zeilen von Primärschlüssel gewartet.
Mit diesen Erkenntnissen habe ich eine Prozedur erstellt, die aus diesem String, der die Wait-Resource identifiziert, das spezifische Objekt, also die Tabelle, und wenn möglich sogar die
betroffenen Zeilen zurückgibt.
Bei dem Typ KEY ist es nämlich möglich durch den in Klammern stehenden Teil die betreffenden Zeilen im SQL Server zu identifizieren.
USE [ADMINDB]GOIF EXISTS (SELECT * FROM sys.objects WHERE object_id = OBJECT_ID(N'[dbo].[ADMIN_Resolve_WaitResource]') AND type in (N'P', N'PC'))
DROP PROCEDURE [dbo].[ADMIN_Resolve_WaitResource]GO
/*
Autor: Patrick Fiedler
Website: http://www.fiedler-sql-consulting.de
Ablauf: Löst die WaitResourcen-Strings auf, wie zum Beispiel:
OBJECT: 2:965199099:16
RID: 6:1:2618395:15
KEY: 7:72057594040811520 (4a98f6ce4bc0)
PAGE: 5:1:13259434
TAB: 4:149575571:0
*/create procedure [dbo].[ADMIN_Resolve_WaitResource]
@WaitResource nvarchar(100),
@ResourceName nvarchar(100) = null output,
@WithTableOutput bit = 0,
@WithInvolvedRowsOutput bit = 1
as
set nocount on
declare @ResourceType nvarchar(50),
@dbid int,
@fileid int,
@pageid bigint,
@hobtid bigint,
@ObjectId int,
@Objectname nvarchar(200),
@IndexId int,
@LockRes nvarchar(50),
@sql nvarchar(max)
set @ResourceType=substring(@WaitResource,1,CHARINDEX(':',@WaitResource)-1)
set @WaitResource=substring(@WaitResource,CHARINDEX(':',@WaitResource)+1,200)
create table #TempPage(
ParentObject nvarchar(200),
Object nvarchar(200),
Field nvarchar(200),
Value nvarchar(max)
)
if(@ResourceType in ('PAGE','PAG','EXT'))
begin
-- Datenbank-ID trennen
set @dbid=SUBSTRING(@WaitResource,1,CHARINDEX(':',@WaitResource)-1)
-- FileID trennen
set @fileid=SUBSTRING(@WaitResource,CHARINDEX(':',@WaitResource)+1,CHARINDEX(':',@WaitResource,CHARINDEX(':',@WaitResource)+1)-CHARINDEX(':',@WaitResource)-1)
-- PageID trennen
set @pageid=reverse(SUBSTRING(REVERSE(@WaitResource),1,charindex(':',reverse(@WaitResource))-1))
set @sql=N'use ['+DB_NAME(@dbid)+']; DBCC PAGE (@dbid, @fileid, @pageid, 3) with tableresults'
begin try
insert into #TempPage
exec sp_executesql @sql, N'@dbid int, @fileid int, @pageid bigint',@dbid=@dbid, @fileid=@fileid, @pageid=@pageid
end try
begin catch
set @sql=N'use ['+DB_NAME(@dbid)+']; DBCC PAGE (@dbid, @fileid, @pageid) with tableresults'
insert into #TempPage
exec sp_executesql @sql, N'@dbid int, @fileid int, @pageid bigint',@dbid=@dbid, @fileid=@fileid, @pageid=@pageid
end catch
set @ObjectId=(select Value from #TempPage where Field='Metadata: ObjectId')
set @IndexId=(select Value from #TempPage where Field='Metadata: IndexId')
set @sql='
use ['+DB_NAME(@dbid)+'];
select @ResourceName=so.name+'' - ''+case when '+CAST(@IndexId as nvarchar)+'=0 then ''Heap'' else isnull(si.name,''N/A'') end,
@ObjectName=so.name from sys.objects so left join
sys.indexes si on so.object_id=si.object_id where so.object_id='+CAST(@ObjectId as nvarchar)+'
and si.index_id='+CAST(@IndexId as nvarchar)
exec sp_executesql @sql, N'@ResourceName nvarchar(100) output, @ObjectName nvarchar(200) output', @ResourceName=@ResourceName out, @ObjectName=@ObjectName out
if(@WithTableOutput=1 and @WithInvolvedRowsOutput=1)
begin
set @LockRes=substring(@WaitResource, charindex('(',@WaitResource), 4000)
set @sql='
use ['+db_name(@dbid)+']; select Comment=''Involved Rows'',
InternalRowId= %%lockres%%, o.* from ( select t.Value
from #TempPage t where t.Field in (''RowId'',''KeyHashValue'') ) x
inner join ['+@Objectname+'] o with (index='+cast(@IndexId as nvarchar)+') on x.Value= %%lockres%% '
exec(@sql)
end
if(@WithTableOutput=1)
begin
select WaitResource=@ResourceType+': '+@WaitResource,
ObjectId=@ObjectId,
IndexId=@IndexId,
ResourceName=@ResourceName
end
end else
if(@ResourceType='KEY')
begin
-- Datenbank-ID trennen
set @dbid=SUBSTRING(@WaitResource,1,CHARINDEX(':',@WaitResource)-1)
-- FileID trennen
if(charindex('(',@WaitResource)>0)
set @hobtid=SUBSTRING(@WaitResource,CHARINDEX(':',@WaitResource)+1,CHARINDEX('(',@WaitResource,CHARINDEX(':',@WaitResource)+1)-CHARINDEX(':',@WaitResource)-1)
else
set @hobtid=SUBSTRING(@WaitResource,CHARINDEX(':',@WaitResource)+1,100)
set @sql='
use ['+DB_NAME(@dbid)+']; SELECT @ObjectId=o.object_id,
@Objectname=o.name, @IndexId=i.index_id,
@ResourceName=o.name + '' - '' + isnull(i.name,''N/A'') FROM sys.partitions p INNER JOIN
sys.objects o ON p.OBJECT_ID = o.OBJECT_ID LEFT JOIN
sys.indexes i ON p.OBJECT_ID = i.OBJECT_ID
AND p.index_id = i.index_id WHERE p.hobt_id = '+cast(@hobtid as nvarchar)+'
'
--print @sql
exec sp_executesql @sql, N'@ObjectId bigint output, @Objectname nvarchar(200) output, @IndexId int output, @ResourceName nvarchar(100) output', @ObjectId=@ObjectId out, @Objectname=@Objectname out, @IndexId=@IndexId out, @ResourceName=@ResourceName out
--print @ResourceName
if(@WithTableOutput=1 and @WithInvolvedRowsOutput=1)
begin
set @LockRes=substring(@WaitResource, charindex('(',@WaitResource), 4000)
set @sql='
use ['+db_name(@dbid)+']; select Comment=''Involved Rows'',
InternalRowId= %%lockres%%, * from ['+@Objectname+'] with (index='+cast(@IndexId as nvarchar)+')
where %%lockres%% = '''+@LockRes+''''
exec(@sql)
end
if(@WithTableOutput=1)
begin
select WaitResource=@ResourceType+': '+@WaitResource,
ObjectId=@ObjectId,
IndexId=@IndexId,
ResourceName=@ResourceName
end
end else
if(@ResourceType in ('OBJECT','TAB'))
begin
-- Datenbank-ID trennen
set @dbid=SUBSTRING(@WaitResource,1,CHARINDEX(':',@WaitResource)-1)
-- ObjectID trennen
set @ObjectId=SUBSTRING(@WaitResource,CHARINDEX(':',@WaitResource)+1,CHARINDEX(':',@WaitResource,CHARINDEX(':',@WaitResource)+1)-CHARINDEX(':',@WaitResource)-1)
set @sql='use ['+DB_NAME(@dbid)+']; set @ResourceName=object_name('+CAST(@ObjectId as nvarchar)+')'
exec sp_executesql @sql, N'@ResourceName nvarchar(100) output', @ResourceName=@ResourceName out
if(@WithTableOutput=1)
begin
select WaitResource=@ResourceType+': '+@WaitResource,
ObjectId=@ObjectId,
IndexId=-1,
ResourceName=@ResourceName
end
end else
if(@ResourceType='RID')
begin
-- Datenbank-ID trennen
set @dbid=SUBSTRING(@WaitResource,1,CHARINDEX(':',@WaitResource)-1)
--print @dbid
-- FileID trennen
set @fileid=SUBSTRING(@WaitResource,CHARINDEX(':',@WaitResource)+1,CHARINDEX(':',@WaitResource,CHARINDEX(':',@WaitResource)+1)-CHARINDEX(':',@WaitResource)-1)
-- ObjectID trennen
set @PageId=reverse(SUBSTRING(reverse(@WaitResource),CHARINDEX(':',reverse(@WaitResource))+1,CHARINDEX(':',reverse(@WaitResource),CHARINDEX(':',reverse(@WaitResource))+1)-CHARINDEX(':',reverse(@WaitResource))-1))
--print @ObjectId
set @sql=N'use ['+DB_NAME(@dbid)+']; DBCC PAGE (@dbid, @fileid, @pageid, 3) with tableresults'
insert into #TempPage
exec sp_executesql @sql, N'@dbid int, @fileid int, @pageid bigint',@dbid=@dbid, @fileid=@fileid, @pageid=@pageid
set @ObjectId=(select Value from #TempPage where Field='Metadata: ObjectId')
set @IndexId=(select Value from #TempPage where Field='Metadata: IndexId')
set @sql='
use ['+DB_NAME(@dbid)+'];
select @ResourceName=so.name+'' - ''+case when '+CAST(@IndexId as nvarchar)+'=0 then ''Heap'' else isnull(si.name,''N/A'') end,
@Objectname=so.name from sys.objects so left join
sys.indexes si on so.object_id=si.object_id and si.index_id='+CAST(@IndexId as nvarchar)+'
where so.object_id='+CAST(@ObjectId as nvarchar)+' '
exec sp_executesql @sql, N'@ResourceName nvarchar(100) output, @Objectname nvarchar(200) output', @ResourceName=@ResourceName out, @Objectname=@Objectname out
if(@WithTableOutput=1 and @WithInvolvedRowsOutput=1)
begin
set @LockRes=substring(@WaitResource, charindex(':',@WaitResource)+1, 4000)
set @sql='
use ['+db_name(@dbid)+']; select Comment=''Involved Rows'',
InternalRowId= %%lockres%%, * from ['+@Objectname+'] where %%lockres%% = '''+@LockRes+''''
exec(@sql)
end
if(@WithTableOutput=1)
begin
select WaitResource=@ResourceType+': '+@WaitResource,
ObjectId=@ObjectId,
IndexId=@IndexId,
ResourceName=@ResourceName
end
end
drop table #TempPage
--select @ResourceName
Kommentar schreiben