sqlserver监控阻塞(死锁)具体情况

  公司sqlserver的监控系统主要是采用zabbix监控,但是zabbix的监控只能通过性能计数器给出报警,而无法给出具体的阻塞情况,比如阻塞会话、语句、时间等,所以需要配合sqlserver的一些特性来进行监控,这里给出一个方案:

  1.创建阻塞日志表,用于记录阻塞情况

  2.新建作业,用于将阻塞情况记录到阻塞日志表中,并发送邮件(如果没有配置邮件,或者不需要发送邮件,可以忽略此步骤)

  3.创建警报,当阻塞大于阈值时,触发上面作业

  在数据库阻塞值大于阈值时,在原有zabbix的监控上,将阻塞报警以短信和邮件方式发送给dba,同时将阻塞信息记录到阻塞记录表中,将阻塞的具体信息通过邮件形式发送给aba,帮助dba进行系统诊断。

  查询阻塞情况依赖于以下sql:

--查询阻塞
SELECT  R.session_id AS BlockedSessionID ,
        S.session_id AS BlockingSessionID ,
        Q1.text AS BlockedSession_TSQL ,
        Q2.text AS BlockingSession_TSQL ,
        C1.most_recent_sql_handle AS BlockedSession_SQLHandle ,
        C2.most_recent_sql_handle AS BlockingSession_SQLHandle ,
        S.original_login_name AS BlockingSession_LoginName ,
        S.program_name AS BlockingSession_ApplicationName ,
        S.host_name AS BlockingSession_HostName
FROM    sys.dm_exec_requests AS R
        INNER JOIN sys.dm_exec_sessions AS S ON R.blocking_session_id = S.session_id
        INNER JOIN sys.dm_exec_connections AS C1 ON R.session_id = C1.most_recent_session_id
        INNER JOIN sys.dm_exec_connections AS C2 ON S.session_id = C2.most_recent_session_id
        CROSS APPLY sys.dm_exec_sql_text(C1.most_recent_sql_handle) AS Q1
        CROSS APPLY sys.dm_exec_sql_text(C2.most_recent_sql_handle) AS Q2

对sql进行测试,表t中只有一条数据。会话1中执行以下sql

会话2执行sql后产生阻塞

用该sql查询的结果:

  对于该sql的字段很简单,blocked开头的表示被阻塞的,blocking表示阻塞的。

一.创建阻塞日志表,用于记录阻塞情况

USE etcp_alert
GO
CREATE TABLE [dbo].[BlockLog]
    (
      Id INT IDENTITY(1, 1)
             NOT NULL
             PRIMARY KEY ,
      [BlockingSessesionId] [smallint] NULL ,
      [ProgramName] [nchar](128) NULL ,
      [HostName] [nchar](128) NULL ,
      [ClientIpAddress] [varchar](48) NULL ,
      [DatabaseName] [sysname] NOT NULL ,
      [WaitType] [nvarchar](60) NULL ,
      [BlockingStartTime] [datetime] NOT NULL ,
      [WaitDuration] [bigint] NULL ,
      [BlockedSessionId] [int] NULL ,
      [BlockedSQLText] [nvarchar](MAX) NULL ,
      [BlockingSQLText] [nvarchar](MAX) NULL ,
      [dt] [datetime] NOT NULL
    )
ON  [PRIMARY]
GO

二、新建作业,用于将阻塞情况记录到阻塞日志表中,并发送邮件

  

  

在新建作业步骤中,选择数据库tempdb,并插入代码:

SET NOCOUNT ON;
DECLARE @dt DATETIME= GETDATE();
 -- 阻塞时间
DECLARE @HtmlContent NVARCHAR(MAX);
 --邮件发送的阻塞日志(表格形式)

IF OBJECT_ID(‘tempdb.dbo.#BlockLog‘) IS NOT NULL
    DROP TABLE #BlockLog;
--将当前日志记录插入临时表
BEGIN
    SELECT  wt.blocking_session_id AS BlockingSessesionId ,
            sp.program_name AS ProgramName ,
            COALESCE(sp.LOGINAME, sp.nt_username) AS HostName ,
            ec1.client_net_address AS ClientIpAddress ,
            db.name AS DatabaseName ,
            wt.wait_type AS WaitType ,
            ec1.connect_time AS BlockingStartTime ,
            wt.WAIT_DURATION_MS / 1000 AS WaitDuration ,
            ec1.session_id AS BlockedSessionId ,
            h1.TEXT AS BlockedSQLText ,
            h2.TEXT AS BlockingSQLText ,
            @dt dt
    INTO    #BlockLog
    FROM    sys.dm_tran_locks AS tl
            INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
            INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
            INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
            INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
            LEFT OUTER JOIN master.dbo.sysprocesses sp ON SP.spid = wt.blocking_session_id
            CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
            CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2;
--将临时表数据插入日志表
    INSERT  INTO etcp_alert.dbo.BlockLog
            ( BlockingSessesionId ,
              ProgramName ,
              HostName ,
              ClientIpAddress ,
              DatabaseName ,
              WaitType ,
              BlockingStartTime ,
              WaitDuration ,
              BlockedSessionId ,
              BlockedSQLText ,
              BlockingSQLText ,
              dt
            )
            SELECT  BlockingSessesionId ,
                    ProgramName ,
                    HostName ,
                    ClientIpAddress ,
                    DatabaseName ,
                    WaitType ,
                    BlockingStartTime ,
                    WaitDuration ,
                    BlockedSessionId ,
                    BlockedSQLText ,
                    BlockingSQLText ,
                    dt
            FROM    #BlockLog;
END;
--以html表格方式发送邮件,如果不发送邮件,则删除以下代码
BEGIN
    SET @HtmlContent = N‘<head>‘
        + N‘<style type="text/css">h2, body {font-family: Arial, verdana;} table{font-size:11px; border-collapse:collapse;} td{background-color:#F1F1F1; border:1px solid black; padding:3px;} th{background-color:#99CCFF;}</style>‘
        + N‘<table border="1">‘ + N‘<tr>
                 <th>BlockingSessesionId</th>
                 <th>ProgramName</th>
                 <th>HostName</th>
                 <th>ClientIpAddress</th>
                 <th>DatabaseName</th>
                 <th>WaitType</th>
                 <th>BlockingStartTime</th>
                 <th>WaitDuration</th>
                 <th>BlockedSessionId</th>
                 <th>BlockedSQLText</th>
                 <th>BlockingSQLText</th>
                 <th>dt</th>
                </tr>‘ + CAST(( SELECT  BlockingSessesionId AS TD ,
                                        ‘‘ ,
                                        ProgramName AS TD ,
                                        ‘‘ ,
                                        HostName AS TD ,
                                        ‘‘ ,
                                        ClientIpAddress AS TD ,
                                        ‘‘ ,
                                        DatabaseName AS TD ,
                                        ‘‘ ,
                                        WaitType AS TD ,
                                        ‘‘ ,
                                        BlockingStartTime AS TD ,
                                        ‘‘ ,
                                        WaitDuration AS TD ,
                                        ‘‘ ,
                                        BlockedSessionId AS TD ,
                                        ‘‘ ,
                                        BlockedSQLText AS TD ,
                                        ‘‘ ,
                                        BlockingSQLText AS TD ,
                                        ‘‘ ,
                                        dt AS Td ,
                                        ‘‘
                                FROM    #BlockLog
                              FOR
                                XML PATH(‘tr‘) ,
                                    TYPE
                              ) AS NVARCHAR(MAX)) + N‘</table>‘;
    IF @HtmlContent IS NOT NULL
        BEGIN
            DECLARE @ProfileName VARCHAR(100)= ‘db_mail‘; --邮箱公用账户名称
            DECLARE @RecipientsLst VARCHAR(100)= ‘[email protected]‘;  --收件人,以";"分隔
            DECLARE @subject VARCHAR(100)= ‘数据库阻塞警报‘; --主题
            EXEC msdb.dbo.sp_send_dbmail @profile_name = @ProfileName,
                @recipients = @RecipientsLst, @subject = @subject,
                @body = @HtmlContent, @body_format = ‘HTML‘;
        END;
    begin
        DROP TABLE #BlockLog;
    END;
END;

注意,如果没有配置邮箱账号,需要配置邮箱功能,如下:

三、创建警报,当阻塞大于阈值时,触发上面作业

名称:可根据实际自行命名,这里我用数据库阻塞报警
类型:选择"SQL Server性能条件警报"
对象:SQLServer:General Statistics
计数器:Processes blocked
计数器满足以下条件时触发警报:高于
值:2,根据系统具体定

在"响应"中配置,一定将执行作业指向上面创建的job

四、测试

为了测试方便,我将报警阈值调整为高于0个,即当1个阻塞发生时就会触发对应的job,还是采用之前的两个会话,查看报警。

邮箱收到报警:

结果表已经插入数据:

时间: 2024-12-16 09:55:46

sqlserver监控阻塞(死锁)具体情况的相关文章

第十六章——处理锁、阻塞和死锁(3)——使用SQLServer Profiler侦测死锁

原文:第十六章--处理锁.阻塞和死锁(3)--使用SQLServer Profiler侦测死锁 前言: 作为DBA,可能经常会遇到有同事或者客户反映经常发生死锁,影响了系统的使用.此时,你需要尽快侦测和处理这类问题. 死锁是当两个或者以上的事务互相阻塞引起的.在这种情况下两个事务会无限期地等待对方释放资源以便操作.下面是死锁的示意图: 本文将使用SQLServer Profiler来跟踪死锁. 准备工作: 为了侦测死锁,我们需要先模拟死锁.本例将使用两个不同的会话创建两个事务. 步骤: 1. 打

SQLServer中的死锁的介绍

原文:SQLServer中的死锁的介绍 简介      什么是死锁? 我认为,死锁是由于两个对象在拥有一份资源的情况下申请另一份资源,而另一份资源恰好又是这两对象正持有的,导致两对象无法完成操作,且所持资源无法释放. 什么又是阻塞? 阻塞是由于资源不足引起的排队等待现象.比如同时两个进程去更新一个表. 这里我们可以把阻塞作为死锁的必要条件.下面我们先理解一下死锁和阻塞再来看一下我最近遇到一个问题以及解决思路. SQLServer中的死锁      对应到SQL Server中,当在两个或多个任务

SQLSERVER查询数据库死锁的存储过程

原文:SQLSERVER查询数据库死锁的存储过程 USE [IdentityDemo] GO /****** Object: StoredProcedure [dbo].[sp_who_lock] Script Date: 2019/1/17 10:47:53 ******/ SET ANSI_NULLS ON GO SET QUOTED_IDENTIFIER ON GO CREATE procedure [dbo].[sp_who_lock] as begin declare @spid in

sqlserver 监控自动化作业执行情况

ALTER procedure [dbo].[monitorJob] @name varchar(100) as begin declare @bd varchar(100) ; if exists( select * from  msdb.dbo.sysjobhistory where job_id in (select job_id from msdb.dbo.sysjobs where [name][email protected] ) and run_date=convert(varch

Opserver配置Redis、SqlServer监控

简介 Opserver是Stack Overflow的开源监控解决方案,由Stack Exchange发布,基于.NET框架构建.开源地址:https://github.com/opserver/Opserver 使用 github下载源代码编译后,发布至IIS,需要先修改Opserver/Config目录下配置信息(根据模板修改): 首先是SecuritySettings.config(编辑后去掉.example后缀): 1 <?xml version="1.0" encodi

使用SQLServer Profiler侦测死锁(转)

准备工作: 为了侦测死锁,我们需要先模拟死锁.本例将使用两个不同的会话创建两个事务. 步骤: 1. 打开SQLServer Profiler 2. 选择[新建跟踪],连到实例. 3. 然后选择[空白]模版: 4. 在[事件选择]页中,展开Locks事件,并选择以下事件: 1. Deadlock graph 2. Lock:Deadlock 3. Lock:Deadlock Chain 5. 然后打开TSQL事件,并选择以下事件: 1. SQL:StmtCompleted 2. SQL:StmtS

30分钟全面解析-SQL事务+隔离级别+阻塞+死锁

以前总是追求新东西,发现基础才是最重要的,今年主要的目标是精通SQL查询和SQL性能优化. 本系列[T-SQL基础]主要是针对T-SQL基础的总结. [T-SQL基础]01.单表查询-几道sql查询题 [T-SQL基础]02.联接查询 [T-SQL基础]03.子查询 [T-SQL基础]04.表表达式-上篇 [T-SQL基础]04.表表达式-下篇 [T-SQL基础]05.集合运算 [T-SQL基础]06.透视.逆透视.分组集 [T-SQL基础]07.数据修改 [T-SQL基础]08.事务和并发 [

sqlserver 查看表死锁

1.SELECT request_session_id spid,OBJECT_NAME(resource_associated_entity_id) tableName FROM sys.dm_tran_locks WHERE resource_type='OBJECT ' spid :被锁进程ID. tableName:发生死锁的表名. 2.只需要使用kill关键字来杀掉被锁的进程ID就可以对表进行解锁: KILL spid 原文地址:https://www.cnblogs.com/ming

死锁的监控分析

死锁的监控分析 阅读目录(Content) 1 背景 1.1 报警情况 1.2 如何监控 2 分析 3 解决 回到顶部(go to top) 1 背景 1.1 报警情况 最近整理笔记,打算全部迁移到EVERNOTE.整理到锁这一部分,里边刚好有个自己记录下来的案例,重新整理分享下给大家. 某日中午,收到报警短信,DB死锁异常,单分钟死锁120个. 死锁的xml文件如下: 1 <deadlock-list> 2 <deadlock victim="process810b00cf8