• (4.7)怎么捕获和记录SQL Server中发生的死锁?


    转自:https://blog.csdn.net/c_enhui/article/details/19498327

    我们知道,可以使用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_TOOLSdb_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_TOOLSdb_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,就可以看到以图形形式展现的死锁信息了。

  • 相关阅读:
    Linux部署springboot项目创建守护进程
    实现数据库安全性控制的方法和主要技术。
    推荐一些学习MySQL的资源
    带你解析MySQL binlog
    一文解决MySQL时区相关问题
    手把手教你看MySQL官方文档
    MySQL关于日期为零值的处理
    关于日期及时间字段的查询
    是时候学习Linux了
    MySQL分区表最佳实践
  • 原文地址:https://www.cnblogs.com/gered/p/9504791.html
Copyright © 2020-2023  润新知