问题描述
我正在使用下面的查询来识别服务器上的阻塞并发送警报,也杀死了该会话(通过SQL Job执行)。 查询使用2个CET表Blcokers和层次结构。
很多时候我都遇到错误并导致工作失败。
错误: NT SERVICE \ SQLSERVERAGENT。声明终止。在语句完成之前,最大递归32767已耗尽。 [SQLSTATE 42000](错误530)。步骤失败。
在研究中,我发现在查询中使用OPTION(MAXRECURSION)提示来纠正错误。 我已经按照下面的步骤包含了HINT for Hierarchy CET表,但仍然出现相同的错误
选择* INTO #BlockingProcess 从层次结构 OPTION(MAXRECURSION 32767)
有人可以建议我在以下代码中进行哪些更改以停止无限递归。
ENITER CODE:
SET nocount ON;
SET concat_null_yields_null OFF
go
CREATE TABLE #sp_who2(
ID INT IDENTITY(1,1) NOT NULL,SPID VARCHAR(4),Status VARCHAR(200),Login VARCHAR(200),HostName VARCHAR(200),BlkBy VARCHAR(4),DBName VARCHAR(200),Command VARCHAR(200),CPUTime VARCHAR(20),DiskIO VARCHAR(20),LastBatch VARCHAR(20),ProgramName VARCHAR(200),SPID2 VARCHAR(4),RequestID VARCHAR(4)
)
INSERT #sp_who2
EXEC sp_who2
--SELECT SPID,BlkBy FROM #sp_who2
DELETE FROM #sp_who2
WHERE BlkBy=' .'
AND SPID NOT IN (SELECT BlkBy FROM #sp_who2 WHERE BlkBy IS NOT NULL)
;WITH Hierarchy(ChildSPID,Generation,BlkBy)
AS
(
SELECT SPID,BlkBy
FROM #sp_who2 AS FirtGeneration
WHERE BlkBy=' .'
UNION ALL
SELECT NextGeneration.SPID,Parent.Generation+1,Parent.ChildSPID
FROM #sp_who2 AS NextGeneration
INNER JOIN Hierarchy AS Parent ON NextGeneration.BlkBy = Parent.ChildSPID
)
SELECT * INTO #BlockingProcess
FROM Hierarchy
OPTION(MAXRECURSION 32767)
SELECT * FROM #BlockingProcess
--loop and kill lead blockers
DECLARE @SPIDGen0 INT
DECLARE @SPIDGen1 INT
DECLARE @ElapsedTimeMSGen0 INT --if NULL,use Gen1
DECLARE @ElapsedTimeMSGen1 INT
DECLARE @SUBJECTKILL VARCHAR(200);
--DECLARE @tableHTMLKILL NVARCHAR(MAX);
WHILE EXISTS(SELECT * FROM #BlockingProcess WHERE BlkBy=' .')
BEGIN
SELECT @SPIDGen0=MIN(ChildSPID) FROM #BlockingProcess WHERE Generation=0
SELECT @SPIDGen1=MIN(ChildSPID) FROM #BlockingProcess WHERE Generation=1 and BlkBy=@SPIDGen0
PRINT @SPIDGen0
PRINT @SPIDGen1
SELECT @ElapsedTimeMSGen0 = BlockingRequest.total_elapsed_time
FROM sys.dm_exec_requests BlockingRequest
WHERE session_id=@SPIDGen0
SELECT @ElapsedTimeMSGen1 = BlockingRequest.total_elapsed_time
FROM sys.dm_exec_requests BlockingRequest
WHERE session_id=@SPIDGen1
PRINT @ElapsedTimeMSGen0
PRINT @ElapsedTimeMSGen1
--If (select count(*) from #BLOCKERS) >= 1
IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1) >= 120000
begin
DECLARE @Subject varchar(100)
SELECT @Subject = 'Blocking Tree Report from ' + @@servername
EXEC msdb.dbo.sp_send_dbmail @body = @tableHTML,@body_format = 'HTML',@profile_name = N'',@recipients = N'',@Subject = @Subject
end
drop table #BLOCKERS
WAITFOR DELAY '00:03'
IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1)>180000 --milliseconds = 3 minutes
--IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1)>60000 --milliseconds = 3 minutes
BEGIN
SELECT @SUBJECTKILL=@@SERVERNAME+' - Lead Blocker Session '+CAST(@SPIDGen0 AS VARCHAR(5))+' Killed'
EXEC msdb.dbo.sp_send_dbmail
@profile_name='',@recipients='',@subject = @SUBJECTKILL,@body = @tableHTML,@body_format = 'HTML'
EXEC('KILL ' + @SPIDGen0)
END
--Skip current SPID and move to next SPID
DELETE FROM #BlockingProcess WHERE ChildSPID = @SPIDGen0
END
IF OBJECT_ID('tempdb..#sp_who2') IS NOT NULL DROP TABLE #sp_who2
IF OBJECT_ID('tempdb..#BlockingProcess') IS NOT NULL DROP TABLE #BlockingProcess
解决方法
改为使用sp_whoisactive。它提供了非常容易理解的视图,以显示正在进行的操作,正在执行的进程或正在阻止的进程。
此外,您还可以按运行时间过滤结果并消除最慢的情况。
但是,您正在做的事情似乎不正确。您具有用于杀死事务的自动化脚本。如果每月一次生成一个发票报告,这需要5分钟,而您却不断将其杀死,该怎么办?如果正在创建索引以阻止指定表上的事务并杀死了该怎么办?
Microsoft完全没有这种功能是有原因的-一个人需要接听电话。
最好使用此例程来识别慢查询并进行修复。
,我观察到,在上述查询中,当并行会话自身阻塞时,它以某种方式超出了最大递归,并且不应将其视为阻塞。
因此,我通过在下面的delete语句中包含OR SPID = BlkBy避免了并行会话
ngOnInit(){
this.dataService.behaviourSubjectObservableVariable.subscribe(data => {
if(type === 'city){
// do somehting
}
if(type === 'department){
// do somehting
}
})
}
此后,查询开始正常工作。
,感谢您的回复。
抱歉回复晚了。
我已经删除了对 CET 表的依赖,因为当每次阻塞都是由于睡眠会话而我之前的脚本无法识别睡眠会话时,我会进入最大递归。 我观察到 exec_requests dmv 没有捕获所有睡眠会话,因此当我有加入 exec_requests dmv 来获取阻塞数据时,脚本如何在睡眠会话阻塞时达到最大递归。
解决方案: 我用临时表替换了 CET,还用 sys.sysprocesses 系统表替换了 exec_requests dmv。这解决了最大递归问题。
此外,我添加了更多过滤器来消除睡眠会话的阻塞。 因此,如果头部阻止程序是一个活动会话并且阻止超过 3 分钟,或者如果头部阻止程序是一个睡眠会话,那么现在查询会杀死头部阻止程序。
以下是修改后的完整查询供您参考。肯定有很多需要修改的地方,如果有的话请提供建议。
SET nocount ON;
SET concat_null_yields_null OFF
go
WITH blockers (spid,blocked,level,batch,lastwaittype,status,hostname,cmd,DBNAME,loginname,open_tran,login_time)
AS (SELECT spid,Cast (Replicate ('0',4-Len (Cast (spid AS VARCHAR)))
+ Cast (spid AS VARCHAR) AS VARCHAR (1000)) AS
LEVEL,Replace (Replace (T.text,Char(10),' '),Char (13),' ') AS
BATCH,R.lastwaittype,R.status,R.hostname,r.cmd,DB_NAME(r.dbid),r.loginame,r.open_tran,r.login_time
FROM sys.sysprocesses R WITH (nolock)
CROSS apply sys.Dm_exec_sql_text(R.sql_handle) T
WHERE ( blocked = 0
OR blocked = spid )
AND EXISTS (SELECT spid,4-Len (Cast (spid AS
VARCHAR
)))
+ Cast (spid AS VARCHAR) AS VARCHAR (
1000))
AS
LEVEL,Char (13
),' ') AS
BATCH,r.login_time
FROM sys.sysprocesses R2 WITH (nolock)
CROSS apply
sys.Dm_exec_sql_text(R.sql_handle) T
WHERE R2.blocked = R.spid
AND R2.blocked <> R2.spid
AND DB_NAME(r.dbid) NOT IN ('msdb'))
UNION ALL
SELECT R.spid,R.blocked,Cast (blockers.level
+ RIGHT (Cast ((1000 + R.spid) AS VARCHAR (100)),4) AS
VARCHAR
(
1000)) AS
LEVEL,' ')
AS BATCH,r.login_time
FROM sys.sysprocesses AS R WITH (nolock)
CROSS apply sys.Dm_exec_sql_text(R.sql_handle) T
INNER JOIN blockers
ON R.blocked = blockers.spid
WHERE R.blocked > 0
AND R.blocked <> R.spid
AND DB_NAME(r.dbid) NOT IN ('msdb'))
SELECT N''
+ Replicate (N'|.......',Len (level)/4 - 2)
+ CASE WHEN (Len (level)/4 - 1) = 0 THEN 'HEAD - ' ELSE '|------ ' END +
Cast (
spid AS VARCHAR (10)) + ' ' + batch AS BLOCKING_TREE,Spid,Status,login_time,Getdate() AS 'RunTime',level
--(Select convert(varchar,total_elapsed_time /60000 ) + ':' + right('0' + convert(varchar(2),(total_elapsed_time /1000) % 60 ),2 ) from sys.dm_Exec_requests) as StartedSince
INTO #BLOCKERS
FROM blockers WITH (nolock)
ORDER BY level ASC
--select * from #BLOCKERS
DECLARE @tableHTML NVARCHAR(MAX);
SET @tableHTML = N'<H1>Blocking Tree Report</H1>' + N'<table border="1" bordercolor="#ff0000" cellspacing="0">' + N'<tr>' +
N'<th>Blocking_Tree</th>' + N'<th>hostname</th>' + N'<th>Status</th>' + N'<th>lastwaittype</th>'+'<th>CurrentTime</th>'
+ N'<th>cmd</th>'
+ N'<th>DatabaseName</th>'
+ N'<th>loginname</th>'
+ N'<th>open_tran</th>'
+ N'<th>login_time</th>'
+ '</tr>' + CAST((
SELECT td = Blocking_Tree,'',td =hostname,td =Status,td =lastwaittype,td =RunTime,td= cmd,td= DBNAME,td= loginname,td=open_tran,td=login_time,''
FROM #BLOCKERS
order by level asc
FOR XML PATH('tr'),TYPE
) AS NVARCHAR(MAX)) + N'</table>';
----------------------------------------------------------------
Insert Into [TOOLS].[dbo].[Lead_Blocker_Log]
select * from #BLOCKERS
------------------------------------------------------
SELECT * INTO #BlockingProcess
FROM #BLOCKERS
--select * from #BlockingProcess
---------------------------------------------------------
--loop and kill lead blockers
--DECLARE @SPIDGen2 INT
DECLARE @SPIDGen0 INT
DECLARE @SPIDGen1 INT
DECLARE @ElapsedTimeMSGen0 INT --if NULL,use Gen1
DECLARE @ElapsedTimeMSGen1 INT
DECLARE @SUBJECTKILL VARCHAR(200)
Declare @sleeping1 VARCHAR (50)
Declare @sleeping2 VARCHAR (50)
--DECLARE @tableHTMLKILL NVARCHAR(MAX);
IF EXISTS(SELECT * FROM #BlockingProcess WHERE Blocked NOt in ('0') )
Begin
SELECT @SPIDGen0=MIN(SPID) FROM #BlockingProcess WHERE Blocked=0
SELECT @SPIDGen1=MIN(SPID) FROM #BlockingProcess WHERE lastwaittype = 'LCK_M_U'
--PRINT @SPIDGen0
---PRINT @SPIDGen1
SELECT @ElapsedTimeMSGen0 = BlockingRequest.total_elapsed_time
FROM sys.dm_exec_requests BlockingRequest
WHERE session_id=@SPIDGen0
SELECT @ElapsedTimeMSGen1 = BlockingRequest.total_elapsed_time
FROM sys.dm_exec_requests BlockingRequest
WHERE session_id=@SPIDGen1
--PRINT @ElapsedTimeMSGen0
--PRINT @ElapsedTimeMSGen1
SELECT @sleeping1 = BlockingRequest.status
FROM sys.dm_exec_sessions BlockingRequest
WHERE session_id=@SPIDGen0
SELECT @sleeping2 = BlockingRequest.status
FROM sys.dm_exec_sessions BlockingRequest
WHERE session_id=@SPIDGen1
--PRINT @sleeping1
--PRINT @sleeping2
If (select count(*) from #BLOCKERS) > 1
IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1) >180000 OR ISNULL (@sleeping1,@sleeping2) = 'sleeping'
begin
DECLARE @Subject varchar(100)
SELECT @Subject = 'Blocking Tree Report from ' + @@servername
EXEC msdb.dbo.sp_send_dbmail @body = @tableHTML,@body_format = 'HTML',@profile_name = N'DBMailProfile',@Subject = @Subject
end
IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1)>180000 OR ISNULL (@sleeping1,@sleeping2) = 'sleeping'--milliseconds = 3 minutes
--IF ISNULL(@ElapsedTimeMSGen0,@ElapsedTimeMSGen1)>60000 --milliseconds = 3 minutes
BEGIN
EXEC ('KILL ' + @SPIDGen0)
SELECT @SUBJECTKILL=@@SERVERNAME+' - Lead Blocker Session '+CAST(@SPIDGen0 AS VARCHAR(5))+' Killed'
EXEC msdb.dbo.sp_send_dbmail
@subject = @SUBJECTKILL,@body = @tableHTML,@body_format = 'HTML'
Print ('KILL ' + Convert (varchar (50),@SPIDGen0))
END
END
--IF OBJECT_ID('tempdb..#sp_who2') IS NOT NULL DROP TABLE #sp_who2
IF OBJECT_ID('tempdb..#BLOCKERS') IS NOT NULL DROP TABLE #BLOCKERS
IF OBJECT_ID('tempdb..#BlockingProcess') IS NOT NULL DROP TABLE #BlockingProcess