RDS SQL Server死锁(Deadlock)系列之五利用Extended Events获取死锁信息

问题引入

在过去很长一段时间,不断有客人会问道:“在事先没有任何跟踪或者监控部署的情况下,阿里云RDS SQL Server有没有办法获取到历史死锁信息,供我们分析?”。在写到RDS SQL Server死锁系列文章之五时,我们就可以使用Extended Events来解决这个问题。

分析问题

Extended Events是微软从SQL Server 2008版本开始引入的,其中有一个默认事件会话是system_health,它的作用是用来收集SQL Server发生的错误信息,以XML格式存储在一个名为sys.dm_xe_session_targets的DMV中,这其中就包含了死锁信息。所以,我们可以利用这个DMV关联另外一个名为sys.dm_xe_sessions的DMV来获取死锁信息。
注意:存储在这两个DMV中的信息会伴随SQL Server服务重启而消失。也就是说,我们无法获取到SQL Server服务重启之前的历史死锁信息。

获取历史死锁信息

为了描述清楚如何获取历史死锁信息,我们可以选择其中任意一条死锁信息加以分析,代码如下:

USE master
GO

-- analysis Extend Event for deadlock
DECLARE
    @deadlock_graph_nvarchar nvarchar(max)
    ,@deadlock_graph_xml xml
;

;WITH RingBufferTarget
AS
(
    SELECT CAST (target_data AS XML) AS target_xml
    FROM sys.dm_xe_session_targets st
        INNER JOIN sys.dm_xe_sessions s
            ON s.address = st.event_session_address
    WHERE s.[name] = 'system_health' and st.target_name = 'ring_buffer'
)
SELECT TOP 1 @deadlock_graph_nvarchar = CAST(T.C.query('.') AS NVARCHAR(MAX))
FROM RingBufferTarget AS A
    CROSS APPLY target_xml.nodes('./RingBufferTarget/event') AS T(C)
WHERE T.C.value('./@name','varchar(200)') = 'xml_deadlock_report'

SELECT
    @deadlock_graph_xml = CAST(REPLACE(REPLACE(@deadlock_graph_nvarchar, '&lt;', '<'),'&gt;', '>') as xml)
;

select @deadlock_graph_xml.query('event/data/value/deadlock')

;WITH deadlock
AS
(
        SELECT
                       OwnerID = T.C.value('@id', 'varchar(50)')
                        ,SPid = T.C.value('(./@spid)[1]','int')
                        ,status = T.C.value('(./@status)[1]','varchar(10)')
                        ,Victim = case when T.C.value('@id', 'varchar(50)') = T.C.value('(./../../victim-list/victimProcess/@id)[1]','varchar(50)') then 1 else 0 end
                        ,LockMode = T.C.value('@lockMode', 'varchar(20)')
                        ,Inputbuf = T.C.value('(./inputbuf/text())[1]','varchar(max)')
                        ,SPName = T.C.value('(./executionStack/frame/@procname)[1]','sysname')
                        ,Hostname = T.C.value('(./@hostname)[1]','sysname')
                        ,Clientapp = T.C.value('(./@clientapp)[1]','varchar(max)')
                        ,LoginName = T.C.value('@loginname', 'varchar(20)')
                        ,Action = T.C.value('(./@transactionname)[1]','varchar(max)')
                        ,TransactionTime = T.C.value('@lasttranstarted', 'datetime')
                        --,*
        FROM @deadlock_graph_xml.nodes('./event/data/value/deadlock/process-list/process') AS T(C)
)
,
keylock
AS
(
        SELECT
                OwnerID = T.C.value('./owner[1]/@id', 'varchar(50)')
                ,KeylockObject = T.C.value('./../@objectname', 'varchar(200)')
                ,Indexname = T.C.value('./../@indexname', 'varchar(200)')
                ,IndexLockMode = T.C.value('./../@mode', 'varchar(20)')
        FROM @deadlock_graph_xml.nodes('./event/data/value/deadlock/resource-list/keylock/owner-list') AS T(C)
)
SELECT
        A.SPid
        ,A.TransactionTime
        ,is_Vitim = A.Victim
        ,A.SPName
        ,A.LockMode
        ,B.Indexname
        ,B.KeylockObject
        ,B.IndexLockMode
        ,A.Inputbuf
        ,A.Hostname
        ,A.LoginName
        ,A.Clientapp
        ,A.Action
        ,status
FROM deadlock AS A
        LEFT JOIN keylock AS B
        ON A.OwnerID = B.OwnerID

从执行查询后的结果来看,我们成功拿到了历史死锁信息(从时间字段来看,死锁发生在8天以前),这些有用的信息包含:锁进程,死锁进程,锁的类型,执行的语句,登录用户等信息,如下截图所示:

我们也可以打开这个死锁信息的xml,如下:

<deadlock>
  <victim-list>
    <victimProcess id="process15ee08" />
  </victim-list>
  <process-list>
    <process id="process15ee08" taskpriority="0" logused="0" waitresource="KEY: 14:72057594038910976 (8194443284a0)" waittime="3906" ownerId="23597" transactionname="user_transaction" lasttranstarted="2017-04-19T21:28:11.050" XDES="0x87141730" lockMode="X" schedulerid="1" kpid="4784" status="suspended" spid="64" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-19T21:28:11.050" lastbatchcompleted="2017-04-19T21:28:11.027" clientapp="Microsoft SQL Server Management Studio - Query" hostname="CHERISH-PC" hostpid="4284" loginname="Cherish-PC\Cherish" isolationlevel="read committed (2)" xactid="23597" currentdb="14" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
      <executionStack>
        <frame procname="" line="8" stmtstart="58" sqlhandle="0x020000008902b6141ee31ae1865c893c9823c9cf5d55fafb" />
        <frame procname="" line="8" stmtstart="156" stmtend="276" sqlhandle="0x020000008e0a8d0d015ffe0258d01a670c6864df6370c807" />
      </executionStack>
      <inputbuf>

BEGIN TRAN
UPDATE dbo.test_deadlock2
SET name = &amp;apos;CC&amp;apos;
WHERE id = 1
;

UPDATE dbo.test_deadlock1
SET name = &amp;apos;CC&amp;apos;
WHERE id = 1
;
COMMIT

   </inputbuf>
    </process>
    <process id="process15f048" taskpriority="0" logused="0" waitresource="KEY: 14:72057594038976512 (8194443284a0)" waittime="503" ownerId="23574" transactionname="user_transaction" lasttranstarted="2017-04-19T21:28:09.450" XDES="0x87140e80" lockMode="X" schedulerid="1" kpid="4864" status="suspended" spid="63" sbid="0" ecid="0" priority="0" trancount="2" lastbatchstarted="2017-04-19T21:28:09.443" lastbatchcompleted="2017-04-19T21:28:09.440" clientapp="Microsoft SQL Server Management Studio - Query" hostname="CHERISH-PC" hostpid="4284" loginname="Cherish-PC\Cherish" isolationlevel="read committed (2)" xactid="23574" currentdb="14" lockTimeout="4294967295" clientoption1="671090784" clientoption2="390200">
      <executionStack>
        <frame procname="" line="9" stmtstart="58" sqlhandle="0x0200000070cad20bef4ae5ada9481eea2eb28415cd7e0c04" />
        <frame procname="" line="9" stmtstart="208" stmtend="328" sqlhandle="0x020000008b7a380c6bf24758d2b29f0eeb276e4f0aa76d8f" />
      </executionStack>
      <inputbuf>

BEGIN TRAN
UPDATE dbo.test_deadlock1
SET name = &amp;apos;CC&amp;apos;
WHERE id = 1
;
WAITFOR DELAY &amp;apos;00:00:05&amp;apos;

UPDATE dbo.test_deadlock2
SET name = &amp;apos;CC&amp;apos;
WHERE id = 1
;
ROLLBACK   </inputbuf>
    </process>
  </process-list>
  <resource-list>
    <keylock hobtid="72057594038910976" dbid="14" objectname="" indexname="" id="lock80153480" mode="X" associatedObjectId="72057594038910976">
      <owner-list>
        <owner id="process15f048" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process15ee08" mode="X" requestType="wait" />
      </waiter-list>
    </keylock>
    <keylock hobtid="72057594038976512" dbid="14" objectname="" indexname="" id="lock80154580" mode="X" associatedObjectId="72057594038976512">
      <owner-list>
        <owner id="process15ee08" mode="X" />
      </owner-list>
      <waiter-list>
        <waiter id="process15f048" mode="X" requestType="wait" />
      </waiter-list>
    </keylock>
  </resource-list>
</deadlock>
时间: 2024-10-17 23:13:16

RDS SQL Server死锁(Deadlock)系列之五利用Extended Events获取死锁信息的相关文章

SQL Server调优系列进阶篇(深入剖析统计信息)

原文:SQL Server调优系列进阶篇(深入剖析统计信息) 前言 经过前几篇的分析,其实大体已经初窥到SQL Server统计信息的重要性了,所以本篇就要祭出这个神器了. 该篇内容会很长,坐好板凳,瓜子零食之类... 不废话,进正题 技术准备 数据库版本为SQL Server2008R2,利用微软的以前的案例库(Northwind)进行分析,部分内容也会应用微软的另一个案例库AdventureWorks 相信了解SQL Server的朋友,对这两个库都不会太陌生. 概念理解 关于SQL Ser

SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行)

原文:SQL Server调优系列玩转篇(如何利用查询提示(Hint)引导语句运行) 前言 前面几篇我们分析了关于SQL Server关于性能调优的一系列内容,我把它分为两个模块. 第一个模块注重基础内容的掌握,共分6篇文章完成,内容涵盖一系列基础运算算法,详细分析了如何查看执行计划.掌握执行计划优化点,并一一列举了日常我们平常所写的T-SQL语句所会应用的运算符.我相信你平常所写的T-SQL语句在这几篇文章中都能找到相应的分解运算符. 第二个模块注重SQL Server执行T-SQL语句的时候

SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行)

原文:SQL Server调优系列玩转篇三(利用索引提示(Hint)引导语句最大优化运行) 前言 本篇继续玩转模块的内容,关于索引在SQL Server的位置无须多言,本篇将分析如何利用Hint引导语句充分利用索引进行运行,同样,还是希望扎实掌握前面一系列的内容,才进入本模块的内容分析. 闲言少叙,进入本篇的内容. 技术准备 数据库版本为SQL Server2012,利用微软的以前的案例库(Northwind)进行分析,部分内容也会应用微软的另一个案例库AdventureWorks. 相信了解S

SQL Server调优系列进阶篇(如何索引调优)

原文:SQL Server调优系列进阶篇(如何索引调优) 前言 上一篇我们分析了数据库中的统计信息的作用,我们已经了解了数据库如何通过统计信息来掌控数据库中各个表的内容分布.不清楚的童鞋可以点击参考. 作为调优系列的文章,数据库的索引肯定是不能少的了,所以本篇我们就开始分析这块内容,关于索引的基础知识就不打算深入分析了,网上一搜一片片的,本篇更侧重的是一些实战项内容展示,希望通过本篇文章各位看官能在真正的场景中找到合适的解决方法足以. 对于索引的使用,我希望的是遇到问题找到合适的解决方法就可以,

SQL Server调优系列进阶篇(如何维护数据库索引)

原文:SQL Server调优系列进阶篇(如何维护数据库索引) 前言 上一篇我们研究了如何利用索引在数据库里面调优,简要的介绍了索引的原理,更重要的分析了如何选择索引以及索引的利弊项,有兴趣的可以点击查看. 本篇延续上一篇的内容,继续分析索引这块,侧重索引项的日常维护以及一些注意事项等. 闲言少叙,进入本篇的主题. 技术准备 数据库版本为SQL Server2012,前几篇文章用的是SQL Server2008RT,内容区别不大,利用微软的以前的案例库(Northwind)进行分析,部分内容也会

RDS SQL Server死锁(Deadlock)系列之四利用Service Broker事件通知捕获死锁

问题引入 在前面三篇文章,我们分别谈到了使用DBCC命令捕获死锁:使用Profiler界面跟踪Deadlock Graph事件捕获死锁和使用脚本自动部署Profiler Trace捕获死锁.这篇文章介绍一个非常有意思的捕获死锁的方法:使用SQL Server Service Broker Event Notification来捕获死锁. Service Broker Event Notification Service Broker Event Notification即使用SQL Server

RDS SQL Server死锁(Deadlock)系列之一使用DBCC捕获死锁

问题引入 在日常运维阿里云RDS SQL Server产品过程中,经常会被客户问道:"应用程序被死锁报错啦?影响很大,到底是哪个进程导致了死锁发生的啊?怎么解决啊?怎么办呀?".从客户一连串的问题中,我们深刻体会到了死锁问题的紧迫性和影响之大.授人予鱼而不如授人予渔,RDS SQL Server死锁系列文章就是为了帮助客人彻底解决死锁问题为初衷而诞生的.本篇文章是系列文章的开篇,主要是讨论如何使用DBCC来捕获死锁信息,内容包括: DBCC捕获死锁 死锁测试 死锁分析 解决方法

RDS SQL Server死锁(Deadlock)系列之二使用Profiler捕获死锁

问题引入 不管是RDS SQL Server还是自建SQL Server数据库,死锁的确是一个非常头疼的问题,上一篇文章我们已经谈到了使用DBCC捕获死锁.这篇文章是以阿里云RDS客户遇到的死锁问题为背景,分享死锁文章系列之二使用Profiler捕获死锁. Profiler捕获死锁 使用Profiler工具的Deadlock graph事件,可以非常方便直观的捕获死锁信息.方法是: 开启MSSQL Profiler:开始 -> 运行 -> 键入profiler 新建Deadlock Gra

RDS SQL Server死锁(Deadlock)系列之三自动部署Profiler捕获死锁

问题引入 系列SQL Server死锁系列文章之二,讲的是如何手动部署Profiler来捕获死锁以及对死锁发时场景重现,这篇文章是将这个手动部署的过程自动化话,实现一键部署,既快捷方便,又简单适用.上一篇文章,参见:使用Profiler捕获死锁. 自动部署Profiler 废话不多说,直接上代码,只需要初始化下面的Profiler停止时间@stop_time即可: use master GO set nocount on declare @trace_folder nvarchar(256) ,