本帖最后由 Cherish_j_wang 于 2012-3-12 17:43 编辑
MSSQL DBA精华总结
之DeadLock Graph Trace文件分析
场景
在MSSQL DBA的工作中,经常会有DeadLock的原因分析的案例。本文的目的在于使用SQL脚本获取DeadLock Graph Trace文件的信息,从而达到对DeadLock的原因分析的目的。
前提
Deadlock_testing.sql
(8.4 KB, 下载次数: 60)
在本次分享开始之前,我们首先必须要用MSSQL ProfilerDeadLock Graph的Trace文件,你可要从本文的附件中下载(Deadlock_testing.sql,然后将后缀名修改为trc),也可要使用下面的方法来自己生成。 1. 开启MSSQL Profiler:开始->运行->键入profiler 2. Trace DeadLock Graph Event:在Profiler窗体中,开启一个Trace->显示所有事件->依次找到Locks->DeadLocak Graph->运行(参见下面的截图,按照字母标号依次点击即可) 注: 我们仅Trace这一个事件就好了,请取消其他无用的多余事件。 3. 在MSSQL中创建测试所要使用到的表
- use test
- GO
- if OBJECT_ID('dbo.test_deadlock1','u') is not null
- drop table dbo.test_deadlock1
- go
- create table dbo.test_deadlock1(
- id int identity(1,1) not null primary key
- ,name varchar(20) null
- );
- if OBJECT_ID('dbo.test_deadlock2','u') is not null
- drop table dbo.test_deadlock2
- go
- create table dbo.test_deadlock2(
- id int identity(1,1) not null primary key
- ,name varchar(20) null
- );
- GO
- insert into dbo.test_deadlock1
- select 'AA'
- UNION ALL
- select 'BB';
- insert into dbo.test_deadlock2
- select 'AA'
- UNION ALL
- select 'BB';
- GO
复制代码
4. 开启一个MSSQL连接(比如SPID为53),在这个连接中执行下面的语句
- use test
- GO
- begin tran
- update dbo.test_deadlock1
- set name = 'CC'
- where id = 1
- ;
- waitfor delay '00:00:05'
- update dbo.test_deadlock2
- set name = 'CC'
- where id = 1
- ;
- rollback
复制代码
5. 再打开另外一个MSSQL连接(比如SPID为54),在此连接中执行下面的语句(4,5之间执行的事件差不要超过5秒)
- use test
- GO
- begin tran
- update dbo.test_deadlock2
- set name = 'CC'
- where id = 1
- ;
- update dbo.test_deadlock1
- set name = 'CC'
- where id = 1
- ;
- commit
复制代码
6. 查看Profiler中的DeadLock Graph Trace到的信息,如下图所示: 7. 保存Trace信息,参加下图:比如将Trace信息保存到I:\Temp\Deadlock_testing.trc
脚本
DeadLockGraph_analysis.sql
(2.98 KB, 下载次数: 49)
在Deadlock_testing.trc文件生成后,接下来最重要的事情就是对这个文件进行分析了,那么如何对一个Trace文件进行分析呢?这个就是我们今天需要分享的重点了,前面的篇幅都是铺垫。我们讲,我们的目的使用SQL脚本对这个文件进行分析,话不多说了,直接上脚本: - use master
- go
- -- declare variables.
- declare
- @file nvarchar(256)
- ;
- select
- @file = N'I:\Temp\Deadlock_testing.trc'
- ;
- WITH DATA
- AS
- (
- /*
- SELECT
- RowNumber = row_number() OVER (ORDER BY StartTime)
- ,DeadlockGraph = CAST(TextData AS xml)
- ,StartTime
- ,spid
- FROM test.dbo.Deadlock_testing WITH (NOLOCK)
- WHERE EventClass = 148*/
-
- SELECT
- --[TraceID] = @trace_id ,
- RowNumber = row_number() OVER (ORDER BY StartTime), -- assign a row number to each deadlock
- [DeadlockGraph]=case when TextData like '<deadlock-list%' then convert(xml, TextData) else null end,
- *
- from ::fn_trace_gettable(@file, default)
- where TextData like '<deadlock-list%'
- )
- ,
- deadlock
- AS
- (
- SELECT
- RowNumber
- ,OwnerID = T.C.value('@id', 'varchar(50)')
- ,SPid = T.C.value('(./@spid)[1]','int')
- ,status = T.C.value('(./@status)[1]','varchar(10)')
- --,PagelockObject = T.C.value('/deadlock-list[1]/deadlock[1]/resource-list[1]/pagelock[1]/@objectname', 'varchar(200)')
- --,DeadlockObject = T.C.value('/deadlock-list[1]/deadlock[1]/resource-list[1]/objectlock[1]/@objectname', 'varchar(200)')
- --,KeylockObject = T.C.value('/deadlock-list[1]/deadlock[1]/resource-list[1]/pagelock[1]/@objectname', 'varchar(200)')
- --,DeadlockObject = T.C.value('/deadlock-list[1]/deadlock[1]/resource-list[1]/objectlock[1]/@objectname', 'varchar(200)')
- ,Victim = case when T.C.value('@id', 'varchar(50)') = T.C.value('./../../@victim','varchar(50)') then 1 else 0 end
- ,LockMode = T.C.value('@lockMode', 'varchar(20)')
- ,DeadlockGraph
- ,Inputbuf = T.C.value('(./inputbuf/text())[1]','varchar(max)')
- ,Code = T.C.value('(./executionStack/frame/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)')
- ,StartTime
- ,TransactionTime = T.C.value('@lasttranstarted', 'datetime')
- --,*
- FROM DATA AS A
- CROSS APPLY DeadlockGraph.nodes('deadlock-list/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)')
- --,owner = T.C.query('.')
- FROM DATA AS A
- CROSS APPLY DeadlockGraph.nodes('deadlock-list/deadlock/resource-list/keylock/owner-list') AS T(C)
- )
- SELECT
- --A.OwnerID
- A.SPid
- ,is_Vitim = A.Victim
- --,A.DeadlockGraph
- ,A.SPName
- ,A.Code
- ,A.LockMode
- ,A.StartTime
- ,B.Indexname
- ,B.KeylockObject
- ,B.IndexLockMode
- ,A.Inputbuf
- ,A.Hostname
- ,A.LoginName
- ,A.Clientapp
- ,A.Action
- ,status
- ,A.TransactionTime
- FROM deadlock AS A
- LEFT JOIN keylock AS B
- ON A.OwnerID = B.OwnerID
- ORDER BY A.RowNumber,A.Victim
复制代码
结果如下:
我们可以从结果中很明确的看到: 1. spid=54号进程是DeadLock的牺牲品 2. 死锁时执行的语句是 spid=54 - UPDATE [dbo].[test_deadlock1] set [name] = @1 WHERE [id]=@2
复制代码 spid=53
UPDATE [dbo].[test_deadlock2] set [name] = @1 WHERE [id]=@2
3. DeadLock时Lock的类型是X锁 4. DeadLock是发生在主键上 …
说明 1. 7中保存Trace文件,你也可以将Trace信息保存到表中。然后将脚本中的第15行~第22行注释拿掉,然后注释掉第24行~第30行,也可以得到同样的信息 2. DeadLock Graph的Trace文件中的textData其实是XML格式,我们的方法就是分析这个XML格式,只是大家需要了解每个节点,每个属性的含义。 3. DeadLock Graph的XML文件也请参见下面的附件(也请将后缀名.sql修改为xml),以供大家参考 4. 解决DeadLock不是文本介绍的重点(当然通过这个方法查明原因后,解决问题是很简单的事情了) 5. 本例中解决DeadLock的方法是:按照相同的顺序访问表 6. spid=54发生DeadLock时,会报告下面的错误 消息1205,级别13,状态51,第8 行 事务(进程ID 54)与另一个进程被死锁在锁资源上,并且已被选作死锁牺牲品。请重新运行该事务。 |