了解sql server的死锁图

Posted

技术标签:

【中文标题】了解sql server的死锁图【英文标题】:Understanding Deadlock graph of sql server 【发布时间】:2015-02-10 19:58:23 【问题描述】:

我需要一些帮助来了解为什么在我的数据库的特定表上发生特定死锁。我对死锁知之甚少,特别是同一张桌子上的死锁。

你能帮忙解决这个僵局吗?我不是在寻找具体的解决方案,我只是想知道为什么会发生这种死锁:

<deadlock-list>
    <deadlock victim="process4aa5b88">
        <process-list>
            <process id="process4aa5b88" taskpriority="0" logused="0" waitresource="PAGE: 14:1:6535" 
            waittime="4912" ownerId="260658" transactionname="UPDATE" lasttranstarted="2015-02-10T09:35:10.040" 
            XDES="0x8006bb70" lockMode="U" schedulerid="8" kpid="2804" status="suspended" spid="221" sbid="0" ecid="5" 
            priority="0" trancount="0" lastbatchstarted="2015-02-10T09:35:09.993" 
            lastbatchcompleted="2015-02-10T09:35:09.993" clientapp=".Net SqlClient Data Provider" hostname="ANP-APP" 
            hostpid="6728" isolationlevel="read committed (2)" xactid="260658" currentdb="14" lockTimeout="4294967295" 
            clientoption1="671088672" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="1" stmtstart="2" sqlhandle="0x02000000c7f3e035aa74a68d308785ac6386d1ee4b1f924e">  
                    UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;     </frame>
                </executionStack>
                <inputbuf>      </inputbuf>
            </process>
            <process id="process4a8b288" taskpriority="0" logused="0" waitresource="PAGE: 14:1:6535" 
            waittime="6287" ownerId="260658" transactionname="UPDATE" lasttranstarted="2015-02-10T09:35:10.040" 
            XDES="0xbf9597b0" lockMode="U" schedulerid="7" kpid="6952" status="suspended" spid="221" sbid="0" ecid="7" 
            priority="0" trancount="0" lastbatchstarted="2015-02-10T09:35:09.993" 
            lastbatchcompleted="2015-02-10T09:35:09.993" clientapp=".Net SqlClient Data Provider" hostname="ANP-APP" 
            hostpid="6728" isolationlevel="read committed (2)" xactid="260658" currentdb="14" lockTimeout="4294967295" 
            clientoption1="671088672" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="1" stmtstart="2" sqlhandle="0x02000000c7f3e035aa74a68d308785ac6386d1ee4b1f924e">  
                    UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;     </frame>
                </executionStack>
                <inputbuf>      </inputbuf>
            </process>
            <process id="process463d948" taskpriority="0" logused="0" waitresource="PAGE: 14:1:6503" 
            waittime="4912" ownerId="260657" transactionname="UPDATE" lasttranstarted="2015-02-10T09:35:10.040" 
            XDES="0xb4595850" lockMode="U" schedulerid="4" kpid="2060" status="suspended" spid="219" sbid="0" ecid="3" 
            priority="0" trancount="0" lastbatchstarted="2015-02-10T09:35:09.993" 
            lastbatchcompleted="2015-02-10T09:35:09.993" clientapp=".Net SqlClient Data Provider" hostname="ANP-APP" 
            hostpid="6728" isolationlevel="read committed (2)" xactid="260657" currentdb="14" lockTimeout="4294967295" 
            clientoption1="671088672" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="1" stmtstart="2" sqlhandle="0x02000000c7f3e035aa74a68d308785ac6386d1ee4b1f924e">  
                    UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;     </frame>
                </executionStack>
                <inputbuf>      </inputbuf>
            </process>
            <process id="process449ebc8" taskpriority="0" logused="0" waitresource="PAGE: 14:1:6503" 
            waittime="6287" ownerId="260657" transactionname="UPDATE" lasttranstarted="2015-02-10T09:35:10.040" 
            XDES="0x80007a70" lockMode="U" schedulerid="1" kpid="6936" status="suspended" spid="219" sbid="0" ecid="5" 
            priority="0" trancount="0" lastbatchstarted="2015-02-10T09:35:09.993" 
            lastbatchcompleted="2015-02-10T09:35:09.993" clientapp=".Net SqlClient Data Provider" hostname="ANP-APP" 
            hostpid="6728" isolationlevel="read committed (2)" xactid="260657" currentdb="14" lockTimeout="4294967295" 
            clientoption1="671088672" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="1" stmtstart="2" sqlhandle="0x02000000c7f3e035aa74a68d308785ac6386d1ee4b1f924e">  
                    UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;     </frame>
                </executionStack>
                <inputbuf>      </inputbuf>
            </process>

            <process id="process4a8b048" taskpriority="0" logused="10000" waittime="4905" schedulerid="7" kpid="6324" 
            status="suspended" spid="219" sbid="0" ecid="0" priority="0" trancount="2" 
            lastbatchstarted="2015-02-10T09:35:09.993" lastbatchcompleted="2015-02-10T09:35:09.993" 
            clientapp=".Net SqlClient Data Provider" hostname="ANP-APP" hostpid="6728" loginname="sa" 
            isolationlevel="read committed (2)" xactid="260657" currentdb="14" lockTimeout="4294967295" 
            clientoption1="671088672" clientoption2="128056">
                <executionStack>
                    <frame procname="adhoc" line="1" stmtstart="2" sqlhandle="0x02000000c7f3e035aa74a68d308785ac6386d1ee4b1f924e">  
                    UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;     </frame>
                </executionStack>
                <inputbuf>   UPDATE ic_seguimiento_contenedor   SET nave_id = M.id_interno, nave_nombre=M.campo2   FROM mantenedor_general M   WHERE ic_seguimiento_contenedor.nave_id = 0   AND ic_seguimiento_contenedor.estado_cod&lt;20 AND M.id_empresa=ic_seguimiento_contenedor.id_empresa AND ic_seguimiento_contenedor.id_empresa=105 AND M.mantenedor=&apos;mant_nave&apos;   AND ltrim(rtrim(M.campo1))=ltrim(rtrim(ic_seguimiento_contenedor.nave_cod)) AND ic_seguimiento_contenedor.key_negocio =&apos;53E010566725403&apos;    </inputbuf>
            </process>
        </process-list>
        <resource-list>
            <pagelock fileid="1" pageid="6535" dbid="14" objectname="icomexvi_dys.dbo.ic_seguimiento_contenedor" id="lock95e1e800" mode="U" associatedObjectId="72057594108248064">
                <owner-list/>
                <waiter-list>
                    <waiter id="process4aa5b88" mode="U" requestType="wait"/>
                </waiter-list>
            </pagelock>
            <pagelock fileid="1" pageid="6535" dbid="14" objectname="icomexvi_dys.dbo.ic_seguimiento_contenedor" id="lock95e1e800" mode="U" associatedObjectId="72057594108248064">
                <owner-list>
                    <owner id="process4a8b048" mode="U"/>
                </owner-list>
                <waiter-list>
                    <waiter id="process4a8b288" mode="U" requestType="wait"/>
                </waiter-list>
            </pagelock>
            <pagelock fileid="1" pageid="6503" dbid="14" objectname="icomexvi_dys.dbo.ic_seguimiento_contenedor" id="lock96c23080" mode="U" associatedObjectId="72057594108248064">
                <owner-list/>
                <waiter-list>
                    <waiter id="process463d948" mode="U" requestType="wait"/>
                </waiter-list>
            </pagelock>
            <pagelock fileid="1" pageid="6503" dbid="14" objectname="icomexvi_dys.dbo.ic_seguimiento_contenedor" id="lock96c23080" mode="U" associatedObjectId="72057594108248064">
                <owner-list>
                    <owner id="process4aa5b88" mode="U"/>
                </owner-list>
                <waiter-list>
                    <waiter id="process449ebc8" mode="U" requestType="wait"/>
                </waiter-list>
            </pagelock>
            <exchangeEvent id="Pipec6a2eac0" WaitType="e_waitPipeGetRow" nodeId="4">
                <owner-list>
                    <owner id="process463d948"/>
                </owner-list>
                <waiter-list>
                    <waiter id="process4a8b048"/>
                </waiter-list>
            </exchangeEvent>
        </resource-list>
    </deadlock>
</deadlock-list>

非常感谢您的帮助! 提前致谢

【问题讨论】:

看起来你有multiple processes trying to update many rows on the same page。深入了解它将取决于了解外部交易中涉及哪些其他语句。我看不到批处理中的其他语句,因此看不到可能导致死锁的原因(单独这应该只是阻塞)。您可能会尝试一些事情,例如更改应用程序以使其一次更新更少的行(也许您可以通过更好的索引来避免页面锁定),和/或消除此语句的并行性(使用OPTION (MAXDOP 1) 我有一个具有复制开销的数据库服务器,其中报告的 sp 最有可能成为受害者。我的解决方案是开始在报告过程中使用 WITH (NOLOCK),而不是关键任务数据。 感谢您的信息并建议@AaronBertrand。那个“sql sentry plan explorer”程序似乎非常强大,可以识别和了解我的问题。我会试一试。即使我可以提高查询性能,首先我想知道发生了什么。作为另一个问题,¿有可能在每个查询中“锁定”所有表,而不是页面?我知道这不是最好的选择,但我只想知道是否存在。非常感谢! 【参考方案1】:

这些是进程:process4aa5b88process4a8b288process463d948process449ebc8process4a8b048

页面 6535 归 process4a8b048 所有,由 process4aa5b88process4a8b288 等待。

页面 6503 归 process4aa5b88 所有,由 process449ebc8process463d948 等待。

并行交换管道Pipec6a2eac0process463d948所有,由process4a8b048等待。

死锁循环是这样的:

    process4aa5b88 等待第 6535 页,归 process4a8b048 所有 process4a8b048 等待 process463d948 拥有的交换管道 process463d948 等待 process4aa5b88 拥有的页面 6503

QED 等待列表中的一个循环 => 死锁

扫描期间存在并行性和页面粒度锁定是缺少索引的明确指示。查看 WHERE 子句,确保您有 SARGable 参数。阅读Index Design Basics 和所有链接的章节。

【讨论】:

非常感谢您的帮助@Remus Rusanu,感谢您的解释,我没有看到我之前遇到的死锁的并行问题,所以我有点困惑。我将继续获取有关此死锁的信息(¿ 或阻止?)再次感谢【参考方案2】:

这并不能回答您的问题,但如果您想实时了解死锁,那么您可以创建一个警报,该警报调用一个向 sql 操作员发送电子邮件的 sp。这很简单。

    创建一个表来保存死锁事件。

    创建表 [dbo].[DeadlockEvents]( [警报时间] [日期时间] NULL, [死锁图] [xml] NULL ) 在 [主要] 上

    使用以下 exec sql 作为步骤 1 创建 SQL 代理作业。

    声明@xml XML; SELECT @xml=N'$(ESCAPE_SQUOTE(WMI(TextData)))'

    插入到 ILMDW.dbo.DeadlockEvents (AlertTime, DeadlockGraph) 值 (getdate(), N'$(ESCAPE_SQUOTE(WMI(TextData)))')

    执行 ILMDW.dbo.MAINTENANCE_Deadlock_Graph @xml

    创建配置如下的 SQL Server 管理/警报。

    然后在发生死锁时让 sp 向您或其他人发送电子邮件 -->

    ALTER PROC [dbo].[MAINTENANCE_Deadlock_Graph] @xml XML 作为 开始 设置无计数;

    声明@body VARCHAR(MAX) 创建表 #victim_list(process_id VARCHAR(100)) 创建表 #processdetails ( id VARCHAR(100), 任务优先级 VARCHAR(100), 已登录 VARCHAR(100), 等待资源 VARCHAR(100), 等待时间 VARCHAR(100), ownerId VARCHAR(100), 交易名称 VARCHAR(100), lasttranstarted VARCHAR(100), XDES VARCHAR(100), lockMode VARCHAR(100), schedulerid VARCHAR(100), kpid VARCHAR(100), 状态 VARCHAR(100), spid VARCHAR(100), sbid VARCHAR(100), ecid VARCHAR(100), 优先 VARCHAR(100), 转数 VARCHAR(100), lastbatchstarted VARCHAR(100), 最后一批完成的 VARCHAR(100), 客户端应用程序 VARCHAR(100), 主机名 VARCHAR(100), hostpid VARCHAR(100), 登录名 VARCHAR(100), 隔离级别 VARCHAR(100), xactid VARCHAR(100), 当前数据库 VARCHAR(100), lockTimeout VARCHAR(100), 客户端选项 1 VARCHAR(100), 客户端选项 2 VARCHAR(100) ) 创建表 #frame_details ( id VARCHAR(100), 过程名称 VARCHAR(100), 行 VARCHAR(100), stmtstart VARCHAR(100), sqlhandle VARCHAR(100) ) 创建表 #frame_values ( id VARCHAR(100), 帧 VARCHAR(最大值) ) 创建表 #input_buffer ( id VARCHAR(100), inputbuf VARCHAR(最大值) ) 创建表 #resource_details_keylock ( hobtid VARCHAR(100), dbid VARCHAR(100), 对象名 VARCHAR(100), 索引名称 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), associatedObjectId VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表#resource_details_objectlock ( 锁定分区 VARCHAR(100), objid VARCHAR(100), 子资源 VARCHAR(100), dbid VARCHAR(100), 对象名 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), associatedObjectId VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表 #resource_details_databaselock ( 子资源 VARCHAR(100), dbid VARCHAR(100), 数据库名称 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表#resource_details_exchangeEvent ( id VARCHAR(100), waitType VARCHAR(100), nodeId VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表#resource_details_metadatalock ( 子资源 VARCHAR(100), 类 VARCHAR(100), dbid VARCHAR(100), 数据库名称 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表#resource_details_pagelock ( 文件 ID VARCHAR(100), pageid VARCHAR(100), dbid VARCHAR(100), 数据库名称 VARCHAR(100), 对象名 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), associatedObjectId VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    创建表#resource_details_ridlock ( pageid VARCHAR(100), dbid VARCHAR(100), 数据库名称 VARCHAR(100), 对象名 VARCHAR(100), lock_id VARCHAR(100), 模式 VARCHAR(100), associatedObjectId VARCHAR(100), owner_id VARCHAR(100), 所有者模式 VARCHAR(100), 服务员 ID VARCHAR(100), 服务员模式 VARCHAR(100), waiter_requestType VARCHAR(100) )

    插入#victim_list SELECT dl.n.value('@victim','VARCHAR(100)') FROM @xml.nodes('TextData/deadlock-list/deadlock') dl(n)

    插入#processdetails 选择 dl.n.value('@id1','VARCHAR(100)') 作为 id, dl.n.value('@taskpriority1','VARCHAR(100)') AS 任务优先级, dl.n.value('@logused1','VARCHAR(100)') AS logused, dl.n.value('@waitresource1','VARCHAR(100)') 作为等待资源, dl.n.value('@waittime1','VARCHAR(100)') AS 等待时间, dl.n.value('@ownerId1','VARCHAR(100)') AS ownerId, dl.n.value('@transactionname1','VARCHAR(100)') AS 交易名, dl.n.value('@lasttranstarted1','VARCHAR(100)') AS lasttranstarted, dl.n.value('@XDES1','VARCHAR(100)') AS XDES, dl.n.value('@lockMode1','VARCHAR(100)') AS lockMode, dl.n.value('@schedulerid1','VARCHAR(100)') AS schedulerid, dl.n.value('@kpid1','VARCHAR(100)') AS kpid, dl.n.value('@status1','VARCHAR(100)') AS 状态, dl.n.value('@spid1','VARCHAR(100)') 作为 spid, dl.n.value('@sbid1','VARCHAR(100)') AS sbid, dl.n.value('@ecid1','VARCHAR(100)') 作为 ecid, dl.n.value('@priority1','VARCHAR(100)') 作为优先级, dl.n.value('@trancount1','VARCHAR(100)') AS trancount, dl.n.value('@lastbatchstarted1','VARCHAR(100)') AS lastbatchstarted, dl.n.value('@lastbatchcompleted1','VARCHAR(100)') AS lastbatchcompleted, dl.n.value('@clientapp1','VARCHAR(100)') 作为客户端应用程序, dl.n.value('@hostname1','VARCHAR(100)') 作为主机名, dl.n.value('@hostpid1','VARCHAR(100)') AS hostpid, dl.n.value('@loginname1','VARCHAR(100)') AS 登录名, dl.n.value('@isolationlevel1','VARCHAR(100)') AS 隔离级别, dl.n.value('@xactid1','VARCHAR(100)') 作为 xactid, dl.n.value('@currentdb1','VARCHAR(100)') AS currentdb, dl.n.value('@lockTimeout1','VARCHAR(100)') AS lockTimeout, dl.n.value('@clientoption11','VARCHAR(100)') AS clientoption1, dl.n.value('@clientoption21','VARCHAR(100)') AS clientoption2 FROM @xml.nodes('//TextData/deadlock-list/deadlock/process-list/process') dl(n)

    插入 #frame_details 选择 dl.n.value('../../@id1','VARCHAR(100)') 作为 id, dl.n.value('@procname1','VARCHAR(100)') AS procname, dl.n.value('@line1','VARCHAR(100)') AS 行, dl.n.value('@stmtstart1','VARCHAR(100)') AS stmtstart, dl.n.value('@sqlhandle1','VARCHAR(100)') AS sqlhandle FROM @xml.nodes('//TextData/deadlock-list/deadlock/process-list/process/executionStack/frame') dl(n)

    插入 #frame_values 选择 dl.n.value('../@id1','VARCHAR(100)') 作为 id, dl.n.value('frame1','VARCHAR(100)') 作为框架 FROM @xml.nodes('//TextData/deadlock-list/deadlock/process-list/process/executionStack') dl(n)

    插入 #input_buffer 选择 dl.n.value('@id1','VARCHAR(100)') 作为 id, dl.n.value('inputbuf1','VARCHAR(100)') AS inputbuf FROM @xml.nodes('//TextData/deadlock-list/deadlock/process-list/process') dl(n)

    插入 #resource_details_keylock 选择 dl.n.value('@hobtid1','VARCHAR(100)') 作为 hobtid, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, dl.n.value('@objectname1','VARCHAR(100)') AS 对象名, dl.n.value('@indexname1','VARCHAR(100)') AS 索引名, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('@associatedObjectId1','VARCHAR(100)') AS associatedObjectId, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/keylock') dl(n)

    插入 #resource_details_objectlock SELECT dl.n.value('@lockpartition1','VARCHAR(100)') 作为锁分区, dl.n.value('@objid1','VARCHAR(100)') AS objid, dl.n.value('@subresource1','VARCHAR(100)') 作为子资源, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, dl.n.value('@objectname1','VARCHAR(100)') AS 对象名, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('@associatedObjectId1','VARCHAR(100)') AS associatedObjectId, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/objectlock') dl(n)

    插入 #resource_details_databaselock SELECT dl.n.value('@subresource1','VARCHAR(100)') 作为子资源, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, db_name(dl.n.value('@dbid1','VARCHAR(100)')) AS dbname, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/databaselock') dl(n)

    插入 #resource_details_exchangeEvent SELECT dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@waitType1','VARCHAR(100)') AS waitType, dl.n.value('@nodeId1','VARCHAR(100)') AS nodeId, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/exchangeEvent') dl(n)

    插入 #resource_details_metadatalock SELECT dl.n.value('@subresource1','VARCHAR(100)') 作为子资源, dl.n.value('@classid1','VARCHAR(100)') AS classid, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, db_name(dl.n.value('@dbid1','VARCHAR(100)')) AS dbname, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/metadatalock') dl(n)

    插入 #resource_details_pagelock 选择 dl.n.value('@fileid1','VARCHAR(100)') 作为文件 ID, dl.n.value('@pageid1','VARCHAR(100)') AS pageid, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, db_name(dl.n.value('@dbid1','VARCHAR(100)')) AS dbname, dl.n.value('@objectname1','VARCHAR(100)') AS 对象名, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('@associatedObjectId1','VARCHAR(100)') AS associatedObjectId, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/pagelock') dl(n)

    插入 #resource_details_ridlock SELECT dl.n.value('@pageid1','VARCHAR(100)') AS pageid, dl.n.value('@dbid1','VARCHAR(100)') AS dbid, db_name(dl.n.value('@dbid1','VARCHAR(100)')) AS dbname, dl.n.value('@objectname1','VARCHAR(100)') AS 对象名, dl.n.value('@id1','VARCHAR(100)') AS lock_id, dl.n.value('@mode1','VARCHAR(100)') AS模式, dl.n.value('@associatedObjectId1','VARCHAR(100)') AS associatedObjectId, dl.n.value('(owner-list/owner)1/@id','VARCHAR(100)') AS owner_id, dl.n.value('(owner-list/owner)1/@mode','VARCHAR(100)') AS owner_mode, dl.n.value('(waiter-list/waiter)1/@id','VARCHAR(100)') AS waiter_id, dl.n.value('(waiter-list/waiter)1/@mode','VARCHAR(100)') AS waiter_mode, dl.n.value('(waiter-list/waiter)1/@requestType','VARCHAR(100)') AS waiter_requestType FROM @xml.nodes('//TextData/deadlock-list/deadlock/resource-list/ridlock') dl(n)

    SELECT @body='进程 ID(受害者)' SELECT @body=@body+''+isnull(process_id,'')+'' from #victim_list 选择 @body=@body+''

    SELECT @body=@body+'流程详情: 进程 ID 数据库名称 进程名 等待时间 锁定模式 转帐 客户端应用程序 主机名 登录名 框架 输入缓冲区 '

    SELECT DISTINCT isnull(pd.id,'') as [id], isnull(db_name(pd.currentdb),'') as [currentdb], isnull(fd.procname,'') as [procname], isnull(pd.waittime,'') as [waittime], isnull(pd.lockMode,'') as [lockMode], isnull(pd.trancount,'') as [trancount], isnull(pd.clientapp,'') as [clientapp], isnull(pd.hostname,'') as [主机名], isnull(pd.loginname,'') as [登录名], isnull(fv.frame,'') as [frame], isnull(ib.inputbuf,'') as [inputbuf] 进入#p_details_temp 来自#processdetails pd 左加入 #frame_details fd on pd.id=fd.id 左连接 #frame_values fv on fd.id=fv.id left join #input_buffer ib on fv.id=ib.id

    选择@body=@body+ ''+id+''+ ''+当前数据库+''+ ''+进程名+''+ ''+等待时间+''+ ''+lockMode+''+ ''+转数+''+ ''+clientapp+''+ ''+主机名+''+ ''+登录名+''+ ''+框架+''+ ''+inputbuf+'' 来自 #p_details_temp 选择 @body=@body+''

    如果存在(SELECT TOP 1 * FROM #resource_details_keylock) 开始 SELECT @body=@body+'键锁: '+ 'hobtid'+ 'dbid,'+ '对象名,'+ '索引名称,'+ 'lock_id,'+ '模式,'+ '关联对象ID,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(hobtid,'')+''+ ''+isnull(dbid,'')+''+ ''+isnull(对象名,'')+''+ ''+isnull(indexname,'')+''+ ''+isnull(lock_id,'')+''+ ''+isnull(mode,'')+''+ ''+isnull(associatedObjectId,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' 来自 #resource_details_keylock 选择@body=@body+'' 结束

    如果存在(SELECT TOP 1 * FROM #resource_details_objectlock) 开始 SELECT @body=@body+'ObjectLock: '+ '锁分区'+ 'objid,'+ '子资源'+ 'dbid,'+ '对象名,'+ 'lock_id,'+ '模式,'+ '关联对象ID,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(lockpartition,'')+''+ ''+isnull(objid,'')+''+ ''+isnull(子资源,'')+''+ ''+isnull(dbid,'')+''+ ''+isnull(对象名,'')+''+ ''+isnull(lock_id,'')+''+ ''+isnull(mode,'')+''+ ''+isnull(associatedObjectId,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' FROM #resource_details_objectlock 选择 @body=@body+'' 结束

    如果存在(SELECT TOP 1 * FROM #resource_details_databaselock) 开始

    SELECT @body=@body+'DatabaseLock:<br><table border="1"> <tr>'+   
     '<th>subresource,</th>'+  
     '<th>dbid,</th>'+  
     '<th>dbname,</th>'+  
     '<th>lock_id,</th>'+  
     '<th>mode,</th>'+  
     '<th>owner_id,</th>'+  
     '<th>owner_mode,</th>'+  
     '<th>waiter_id,</th>'+  
     '<th>waiter_mode,</th>'+  
     '<th>waiter_requestType,</th>'+  
     '</tr>'  
    SELECT @body=@body+'<tr>'+   
     '<td>'+isnull(subresource,'')+'</td>'+  
     '<td>'+isnull(dbid,'')+'</td>'+  
     '<td>'+isnull(dbname,'')+'</td>'+  
     '<td>'+isnull(lock_id,'')+'</td>'+  
     '<td>'+isnull(mode,'')+'</td>'+  
     '<td>'+isnull(owner_id,'')+'</td>'+  
     '<td>'+isnull(owner_mode,'')+'</td>'+  
     '<td>'+isnull(waiter_id,'')+'</td>'+  
     '<td>'+isnull(waiter_mode,'')+'</td>'+  
     '<td>'+isnull(waiter_requestType,'')+'</td>'+  
     '</tr>'  
    from #resource_details_databaselock   
    select @body=@body+'</table><br/><br/>'   
    

    结束

    如果存在(选择前 1 * FROM #resource_details_exchangeEvent) 开始 SELECT @body=@body+'ExchangeEvent: '+ 'lock_id,'+ 'waitType,'+ 'nodeId,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(id,'')+''+ ''+isnull(waitType,'')+''+ ''+isnull(nodeId,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' FROM #resource_details_exchangeEvent 选择 @body=@body+'' 结束

    如果存在(SELECT TOP 1 * FROM #resource_details_metadatalock) 开始 SELECT @body=@body+'MetadataLock: '+ '子资源'+ 'classid,'+ 'dbid,'+ '数据库名称,'+ 'lock_id,'+ '模式,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(子资源,'')+''+ ''+isnull(classid,'')+''+ ''+isnull(dbid,'')+''+ ''+isnull(dbname,'')+''+ ''+isnull(lock_id,'')+''+ ''+isnull(mode,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' FROM #resource_details_metadatalock 选择 @body=@body+'' 结束

    如果存在(SELECT TOP 1 * FROM #resource_details_pagelock) 开始 SELECT @body=@body+'PageLock: '+ 'fileid,'+ 'pageid,'+ 'dbid,'+ '数据库名称,'+ '对象名,'+ 'lock_id,'+ '模式,'+ '关联对象ID,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(fileid,'')+''+ ''+isnull(pageid,'')+''+ ''+isnull(dbid,'')+''+ ''+isnull(dbname,'')+''+ ''+isnull(对象名,'')+''+ ''+isnull(lock_id,'')+''+ ''+isnull(mode,'')+''+ ''+isnull(associatedObjectId,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' FROM #resource_details_pagelock 选择 @body=@body+'' 结束

    如果存在(SELECT TOP 1 * FROM #resource_details_ridlock) 开始 SELECT @body=@body+'RidLock: '+ 'pageid,'+ 'dbid,'+ '数据库名称,'+ '对象名,'+ 'lock_id,'+ '模式,'+ '关联对象ID,'+ 'owner_id,'+ 'owner_mode,'+ '服务员 ID,'+ '服务员模式'+ 'waiter_requestType,'+ '' 选择@body=@body+''+ ''+isnull(pageid,'')+''+ ''+isnull(dbid,'')+''+ ''+isnull(dbname,'')+''+ ''+isnull(对象名,'')+''+ ''+isnull(lock_id,'')+''+ ''+isnull(mode,'')+''+ ''+isnull(associatedObjectId,'')+''+ ''+isnull(owner_id,'')+''+ ''+isnull(owner_mode,'')+''+ ''+isnull(waiter_id,'')+''+ ''+isnull(waiter_mode,'')+''+ ''+isnull(waiter_requestType,'')+''+ '' 来自 #resource_details_ridlock 选择 @body=@body+'' 结束

    SELECT @body=@body+'原始 XML'+ 替换(替换(转换(VARCHAR(MAX),@xml),'','>')+ ''

    声明@email_distribution_list VARCHAR(1024) SELECT @email_distribution_list = 'ross@ilearn.com;brad@ilearn.com;kenny@ilearn.com;spenser@ilearn.com'

    执行 msdb.dbo.sp_send_dbmail @profile_name = 'SQL 通知', @recipients=@email_distribution_list, @body = @body, @body_format='html', @subject = '警告!服务器发生死锁, @importance = '高' ;

    删除表#victim_list 删除表 #processdetails 删除表 #frame_details 删除表 #frame_values 删除表 #input_buffer 删除表 #resource_details_databaselock 删除表 #resource_details_exchangeEvent 删除表 #resource_details_keylock 删除表 #resource_details_metadatalock 删除表 #resource_details_objectlock 删除表 #resource_details_pagelock 删除表 #resource_details_ridlock 删除表#p_details_temp

    结束

【讨论】:

感谢@lrb 的帮助,我以前没有处理过警报,所以我是个新手。无论如何我会试一试。谢谢!

以上是关于了解sql server的死锁图的主要内容,如果未能解决你的问题,请参考以下文章

SQL Server死锁图:请解释一下

SQL Server Profiler 中的死锁图显示同一集群键上的互锁

Sql server 死锁排查

sql server 死锁排查

SQL Server 上的死锁跟踪

SQL Server 事务与锁