为什么系统数据库上的 dbcc checkdb 和用户数据库上的 sp_executesql 会导致死锁?

Why do dbcc checkdb on system databases and sp_executesql on a user database cause a deadlock?

我有一份日常工作 运行在每个系统数据库上执行一个没有附加参数的 dbcc checkdb 语句。此作业在非高峰时段 运行 秒,通常需要 5 秒或更短的时间才能 运行。

然而,最后一个运行只用了1秒就因为死锁而失败了。我收到一个警报,它为我保存了一个 xml 死锁图表,我将其包括在内以获取更详细的信息。

我的主要问题是:为什么会出现这样的死锁,是否可以避免?

    <TextData>
      <deadlock-list>
     <deadlock victim="process290fd861088">
      <process-list>
       <process id="process290fd861088" taskpriority="0" logused="0" waitresource="OBJECT: 2:5:0 " ownerId="1250115008" transactionname="CheckDb" lasttranstarted="2017-03-20T01:00:01.427" XDES="0x2b277040bd8" lockMode="S" schedulerid="7" kpid="12760" status="suspended" spid="78" sbid="0" ecid="0" priority="0" trancount="1" lastbatchstarted="2017-03-20T01:00:00.060" lastbatchcompleted="2017-03-20T01:00:00.060" lastattention="1900-01-01T00:00:00.060" clientapp="SQLAgent - TSQL JobStep (Job 0xB425122DD6C28D4BBE42D7F0AF76FC40 : Step 1)" hostname="0000-DB-0000" hostpid="8040" loginname="0000[=11=]00" isolationlevel="read committed (2)" xactid="1250115008" currentdb="2" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
        <executionStack>
         <frame procname="0000_Local.server.CheckSystemDatabases" line="19" stmtstart="740" stmtend="776" sqlhandle="0x030006006a934a11b3ebcd0023a7000001000000000000000000000000000000000000000000000000000000">
    dbcc checkdb(@dbId     </frame>
         <frame procname="adhoc" line="1" stmtend="70" sqlhandle="0x010006006688101b405fcfceb602000000000000000000000000000000000000000000000000000000000000">
    exec [server].[CheckSystemDatabases     </frame>
        </executionStack>
        <inputbuf>
    exec [server].[CheckSystemDatabases];    </inputbuf>
       </process>
       <process id="process2b59a715468" taskpriority="0" logused="952" waitresource="OBJECT: 2:3:0 " ownerId="1250114957" transactionname="droptemp" lasttranstarted="2017-03-20T01:00:01.423" XDES="0x29b8755ce58" lockMode="IX" schedulerid="8" kpid="9440" status="suspended" spid="67" sbid="0" ecid="0" priority="0" trancount="0" lastbatchstarted="2017-03-20T01:00:01.410" lastbatchcompleted="2017-03-20T01:00:01.410" lastattention="1900-01-01T00:00:00.410" clientapp="0000-API-0000" hostname="0000-0000-WEB-0000" hostpid="42180" loginname="0000[=11=]00" isolationlevel="read committed (2)" xactid="0" currentdb="9" lockTimeout="4294967295" clientoption1="673185824" clientoption2="128056">
        <executionStack>
         <frame procname="mssqlsystemresource.sys.sp_executesql" line="1" stmtstart="-1" sqlhandle="0x0400ff7f427f99d9010000000000000000000000000000000000000000000000000000000000000000000000">
    sp_executesql     </frame>
         <frame procname="0000.dbo.SomeProcName" line="93" stmtstart="8320" stmtend="8496" sqlhandle="0x030009002ac137082fa8b20029a7000001000000000000000000000000000000000000000000000000000000">
    exec sp_executesql @selectSql, N'@rowcount int output', @rowcount = @TotalRowCount outpu     </frame>
        </executionStack>
        <inputbuf>
    Proc [Database Id = 9 Object Id = 137871658]    </inputbuf>
       </process>
      </process-list>
      <resource-list>
       <objectlock lockPartition="0" objid="5" subresource="FULL" dbid="2" objectname="tempdb.sys.sysrowsets" id="lock2b4103b8380" mode="IX" associatedObjectId="5">
        <owner-list>
         <owner id="process2b59a715468" mode="IX" />
        </owner-list>
        <waiter-list>
         <waiter id="process290fd861088" mode="S" requestType="wait" />
        </waiter-list>
       </objectlock>
       <objectlock lockPartition="0" objid="3" subresource="FULL" dbid="2" objectname="tempdb.sys.sysrscols" id="lock291f3d8a900" mode="S" associatedObjectId="3">
        <owner-list>
         <owner id="process290fd861088" mode="S" />
        </owner-list>
        <waiter-list>
         <waiter id="process2b59a715468" mode="IX" requestType="wait" />
        </waiter-list>
       </objectlock>
      </resource-list>
     </deadlock>
    </deadlock-list></TextData>

我已经实施的解决方案是将用户事务优先于 TempDB 的 Daily checkDB:

set nocount on;  
set deadlock_priority low;  
 declare @dbId int;  
declare loopCheckDB cursor fast_forward   
for select [d].[database_id] from [sys].[databases] as [d] where   [d].[database_id] < 5  
 order by [d].[name]  
 open [loopCheckDB]   
fetch next from [loopCheckDB] into @dbId;   
while @@FETCH_STATUS = 0  
 begin dbcc checkdb(@dbId); 
fetch next from [loopCheckDB] into @dbId; 
end 
close [loopCheckDB];
 deallocate [loopCheckDB]; 

Why do dbcc checkdb on system databases and sp_executesql on a user database cause a deadlock?

DBCC checkDB 在 tempdb.sys.sysrowsets 上获得了 Intent Exclusive 锁,正在等待 tempdb.sys.sysrscols..

上的共享锁

您的用户进程也在访问 TEMPDB resources.This 用户进程在 tempdb.sys.sysrscols 上获得了 IX 锁,正在等待 tempdb.sys.sysrowsets..

上的共享锁

因此发生了死锁,这是一个简单的死锁案例

通常,DBCC CheckDB 在分析之前拍摄数据库快照,并在该快照上工作以避免锁定、阻塞..

在这种情况下,as per this post..无法使用 TEMPDB 进行快照,因此在您的情况下,两个事务都获得了不兼容的锁,这就是您看到的死锁的原因。