1

我的日常工作是在每个系统数据库上运行没有附加参数的 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\0000" 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\0000" 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]; 
4

1 回答 1

0

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

DBCC checkDB 获得 Intent Exclusive lock ontempdb.sys.sysrowsets并正在等待 sharedlock on tempdb.sys.sysrscols..

您的用户 proc 也在访问 TEMPDB 资源。此用户 proc 获得了 tempdb.sys.sysrscols 上的 IX 锁,并正在等待 tempdb.sys.sysrowsets 上的共享锁。

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

通常,DBCC CheckDB 会在分析之前对数据库进行快照,并在此快照上工作以避免锁定、阻塞。

在这种情况下,根据这篇文章.. TEMPDB 无法进行快照,因此在您的情况下,两个事务都获取了不兼容的锁,这就是您看到的死锁的原因。

于 2017-03-20T11:02:34.450 回答