在语句完成之前,最大递归32767已耗尽 [SQLSTATE 42000]错误530

问题描述

我正在使用下面的查询来识别服务器上的阻塞并发送警报,也杀死了该会话(通过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

相关问答

错误1:Request method ‘DELETE‘ not supported 错误还原:...
错误1:启动docker镜像时报错:Error response from daemon:...
错误1:private field ‘xxx‘ is never assigned 按Alt...
报错如下,通过源不能下载,最后警告pip需升级版本 Requirem...