sql server死锁跟踪

时间:2024-03-10 19:38:17

我们知道,可以使用SQL Server自带的Profiler工具来跟踪死锁信息。但这种方式有一个很大的敝端,就是消耗很大。据国外某大神测试,profiler甚至可以占到服务器总带宽的35%,所以,在一个繁忙的系统中,使用profiler显然不是一个好主意,下面我介绍两种消耗比较少的方法。其中第二种的消耗最小,在最繁忙的系统中也可使用。第一种最为灵活,可满足多种应用。

 

方法一:利用SQL Server代理(Alert+Job)

具体步骤如下:

1.启动跟踪

首先使用下面的命令,将有关的跟踪标志启用。

DBCC TRACEON (3605,1204,1222,-1)  

说明:

3605 将DBCC的结果输出到错误日志。

1204 返回参与死锁的锁的资源和类型,以及受影响的当前命令。

1222 返回参与死锁的锁的资源和类型,以及使用了不符合任何 XSD 架构的 XML 格式的受影响的当前命令(比1204更进一步,SQL 2005及以上可用)。

-1 以全局方式打开指定的跟踪标记。

 

以上跟踪标志作用域都是全局,即在SQL Server运行过程中,会一直发挥作用,直到SQL Server重启。

如果要确保SQL Server在重启后自动开启这些标志,可以在SQL Server服务启动选项中,使用 /T 启动选项指定跟踪标志在启动期间设置为开。(位于SQL Server配置管理器->SQL Server服务->SQL Server->属性->高级->启动参数)

 

在运行上面的语句后,当SQL Server中发生死锁时,已经可以在错误日志中看到了,但还不够直观(和其它信息混在一起)。(SSMS -> SQL Server实例 -> 管理 -> SQL Server日志)

 

2.建表,存放死锁记录 

USE [Cole] --Cole是我的示例数据库,你可以根据实际情况修改。  
GO  
CREATE TABLE DeadLockLog (  
id int IDENTITY (1, 1) NOT NULL,   
LogDate DATETIME,   
ProcessInfo VARCHAR(10),   
ErrorText VARCHAR(MAX)  
)  
GO  

 

3.建立JOB

新建一个JOB(假设名称为DeadLockJob),在"步骤"中新建一步骤,随便写一个步骤名称,数据库为"Cole"(见2.建表),在"命令"栏中输入以下语句:

--新建临时表  
IF OBJECT_ID(\'tempdb.dbo.#ErrorLog\') IS Not Null  
DROP TABLE #ErrorLog  
CREATE TABLE #ErrorLog (Id int IDENTITY (1, 1) NOT NULL, a DATETIME, b VARCHAR(10), c VARCHAR(MAX))  
--将当前日志记录插入临时表  
INSERT INTO #ErrorLog EXEC master.dbo.sp_readerrorlog  
--将死锁信息插入用户表  
insert DeadLockLog  
select a, b, c   
from #ErrorLog   
where id >= (select MAX(id) from #ErrorLog WHERE c Like \'%Deadlock encountered%\')  
DROP TABLE #ErrorLog  

 

或者可以用下面这种办法

--new注释

--找到死锁与阻塞的原因的方法:
if exists (select * from dbo.sysobjects
where id = object_id(N\'[dbo].[sp_who_lock]\')
and OBJECTPROPERTY(id, N\'IsProcedure\') = 1)
drop procedure [dbo].[sp_who_lock]
GO
--说明 : 查看数据库里阻塞和死锁情况
use master
go
create procedure sp_who_lock
as
begin
declare @spid int,@bl int,
@intTransactionCountOnEntry  int,
@intRowcount   int,
@intCountProperties   int,
@intCounter      int
create table #tmp_lock_who
( id
int identity(1,1), spid smallint, bl smallint
)
IF @@ERROR!=0
RETURN @@ERROR
insert into #tmp_lock_who(spid,bl)
select 0 ,blocked from (select * from sysprocesses where blocked>0 ) a where not exists
(select * from
(select * from sysprocesses where blocked>0 ) b where a.blocked=spid
)
union

select spid,blocked from sysprocesses where blocked>0 IF @@ERROR!=0
RETURN @@ERROR
-- 找到临时表的记录数 select @intCountProperties = Count(*),@intCounter = 1 from #tmp_lock_who IF @@ERROR!=0 RETURN @@ERROR if @intCountProperties=0
select \'现在没有阻塞和死锁信息\' as message -- 循环开始 while @intCounter <= @intCountProperties begin -- 取第一条记录 select @spid = spid,@bl = bl from #tmp_lock_who where Id = @intCounter begin if @spid =0 select \'引起数据库死锁的是: \'+ CAST(@bl AS VARCHAR(10)) + \'进程号,其执行的SQL语法如下\' else select \'进程号SPID:\'+ CAST(@spid AS VARCHAR(10))+ \'\' + \'进程号SPID:\'+ CAST(@bl AS VARCHAR(10)) +\'阻塞,其当前进程执行的SQL语法如下\' DBCC INPUTBUFFER (@bl ) end -- 循环指针下移 set @intCounter = @intCounter + 1 end drop table #tmp_lock_who return 0 end --需要的时候直接调用,就可以查出引起死锁的进程和SQL语句. exec sp_who_lock 发现问题后:就进行语句的优化,避免来再次出现上面现象。Profiler跟踪时主要是看:TextData,Applicationname,Username,Loginname,CpU,Read and write Duration(这个很重要),spid

 

4.新建警报

 

在"新建警报"窗体的"常规"选项卡中,进行以下设置:

名称:可根据实际自行命名,这里我用DeadLockAlert

类型:选择"SQL Server性能条件警报"

对象:SQLServer:Locks

计数器:Number of Deadlocks/sec

实例:_Total

计数器满足以下条件时触发警报:高于

值:0

设置完成后,应该如下图所示:

  

 

 

在"响应"选项卡中,选中"执行作业",并选择步骤3中我们新建的作业(即DeadlockJob)

   

 

到这里为止,我们已经完成了全部步骤,以后,你就可以随时查询DeadLockLog表,来显示死锁信息了。

 

方法二:利用服务器端跟踪(推荐)

具体实现步骤如下:

1.编写跟踪脚本并执行

 

use db_tank
go

    --创建跟踪文件返回值
    declare @rc int
    --创建一个跟踪句柄
    declare @TraceID int
    --创建跟踪文件路径
    declare @TraceFilePath nvarchar(500)
    set @TraceFilePath=N\'D:\DBA_TOOLS\db_deadLock_log\'
    --跟踪文件的大小
    declare @maxfilesize bigint
    set @maxfilesize=200
    --设置停止的时间
    declare @EndTime datetime
    set @EndTime=null
    --设置系统默认的操作
    declare @options int
    set @options=2
    --设置默认滚动文件的数目
    declare @filecount int
    set @filecount=5

    exec @rc=sp_trace_Create
    @TraceID output,
    @options,
    @TraceFilePath,
    @maxfilesize,
    @EndTime,
    @filecount
    if(@rc=0)


    declare @on bit  
    set @on = 1  
    --下述语句中的148指的是locks:deadlock graph事件(参见sys.trace_events),12指的是spid列(参见sys.trace_columns)  
    exec sp_trace_setevent @TraceID, 148, 12, @on    
    exec sp_trace_setevent @TraceID, 148, 11, @on  
    exec sp_trace_setevent @TraceID, 148, 4, @on  
    exec sp_trace_setevent @TraceID, 148, 14, @on  
    exec sp_trace_setevent @TraceID, 148, 26, @on  
    exec sp_trace_setevent @TraceID, 148, 64, @on  
    exec sp_trace_setevent @TraceID, 148, 1, @on  
    -- 启动跟踪  
    exec sp_trace_setstatus @TraceID, 1  
    -- 记录下跟踪ID,以备后面使用  
    select TraceID = @TraceID  
    goto finish  
    error:   
    select ErrorCode=@rc  
    finish:   
go  


-----默认重启实例后,跟踪会取消,所以写一个SP做实例启动执行
use master

go
create proc StartBlackBoxTrace
as
begin
                --默认开启追踪所有的SQL 执行语句,文件文件路径为默认
               --创建跟踪文件返回值
            declare @rc int
            --创建一个跟踪句柄
            declare @TraceID int
            --创建跟踪文件路径
            declare @TraceFilePath nvarchar(500)
            set @TraceFilePath=N\'D:\DBA_TOOLS\db_deadLock_log\'
            --跟踪文件的大小
            declare @maxfilesize bigint
            set @maxfilesize=200
            --设置停止的时间
            declare @EndTime datetime
            set @EndTime=null
            --设置系统默认的操作
            declare @options int
            set @options=2
            --设置默认滚动文件的数目
            declare @filecount int
            set @filecount=5

            exec @rc=sp_trace_Create
            @TraceID output,
            @options,
            @TraceFilePath,
            @maxfilesize,
            @EndTime,
            @filecount
            if(@rc=0)
            select  @TraceID


            declare @on bit  
            set @on = 1  
            --下述语句中的148指的是locks:deadlock graph事件(参见sys.trace_events),12指的是spid列(参见sys.trace_columns)  
            exec sp_trace_setevent @TraceID, 148, 12, @on    
            exec sp_trace_setevent @TraceID, 148, 11, @on  
            exec sp_trace_setevent @TraceID, 148, 4, @on  
            exec sp_trace_setevent @TraceID, 148, 14, @on  
            exec sp_trace_setevent @TraceID, 148, 26, @on  
            exec sp_trace_setevent @TraceID, 148, 64, @on  
            exec sp_trace_setevent @TraceID, 148, 1, @on  
            -- 启动跟踪  
            exec sp_trace_setstatus @TraceID, 1  
END
GO
--将该存储过程设置为SQL Server服务启动时自动启动
EXEC sp_procoption
\'StartBlackBoxTrace\',\'STARTUP\',\'ON\'
print \'ok\'
GO


--查看
select * from sys.traces

--删除
exec sp_trace_setstatus 2, 0
exec sp_trace_setstatus 2, 2

运行上述语句后,每当SQL Server中发生死锁事件,都会自动往文件e:/DbLog/deadlockdetect.trc中插入一条记录。

2.暂停和停止服务器端跟踪

如果要启动上面的服务端跟踪,可运行下面的语句:

exec sp_trace_setstatus 2, 1 --第一个参数2表示 TraceID,可以通过select * from sys.traces查看跟踪ID。第二个参数表示将状态改为1,即启动

如果要停止上面的服务器端跟踪,可运行下面的语句:

exec sp_trace_setstatus 1, 0 --第一个参数表示TraceID,即步骤1中的输出参数。第二个参数表示将状态改为0,即暂停

如果要移除上面的服务器端跟踪,可运行下面的语句:

exec sp_trace_setstatus 1, 2 --第一个参数表示TraceID,即步骤1中的输出参数。第二个参数表示将状态改为2,即停止

 

3.查看跟踪文件内容

对于上面生成的跟踪文件(e:/DbLog/deadlockdetect.trc),可通过两种方法查看:

1).执行t-sql命令

select * from fn_trace_gettable(\'e:/DbLog/deadlockdetect.trc\',1)  

结果中的TextData列即以XML的形式返回死锁的详细信息。

2).在SQL Server Profiler中打开。

依次 进入Profiler -> 打开跟踪文件 ->选择e:/DbLog/deadlockdetect.trc,就可以看到以图形形式展现的死锁信息了。