背景

我们在数据库出现阻塞及时邮件预警提醒中监控了数据库的阻塞情况,为了更好的维护数据库,特别是提升终端客户用户体验,我们要尽量避免在数据库中出现死锁的情况。我们知道收集死锁可以开启跟踪标志如1204,然后在日志中查看死锁相关信息,或者使用Profiler去跟踪死锁,我们希望所有的死锁信息收集到某表供我们后期优化分析使用,我们可以使用相对比较轻量的自带扩展事件(system_health)来完成这个需求。

测试环境

Microsoft SQL Server 2012 - 11.0.2100.60 (X64) 
Feb 10 2012 19:39:15 
Copyright (c) Microsoft Corporation
Enterprise Edition (64-bit) on Windows NT 6.2 <X64> (Build 9200: )

实现过程

a.新建存放死锁的表

IF DB_ID('azure_monitor') IS NULL
BEGIN
CREATE DATABASE azure_monitor ;
END
GO ALTER DATABASE azure_monitor SET RECOVERY SIMPLE;
GO USE [azure_monitor]; --存放死锁信息的库名
GO IF OBJECT_ID('monitor_deadlock', 'U') IS NOT NULL
DROP TABLE dbo.monitor_deadlock;
GO
CREATE TABLE [dbo].[monitor_deadlock]
(
[ServerName] [VARCHAR](50),
[DataBaseName] [NVARCHAR](100) NULL ,
[DeadlockID] [BIGINT] NULL ,
[TransactionTime] [DATETIME] NULL ,
[DeadlockGraph] [XML] NULL ,
[DeadlockObjects] [NVARCHAR](MAX) NULL ,
[Victim] [INT] NOT NULL ,
[SPID] [INT] NULL ,
[ProcedureName] [VARCHAR](200) NULL ,
[LockMode] [CHAR](1) NULL ,
[Code] [VARCHAR](1000) NULL ,
[ClientApp] [NVARCHAR](245) NULL ,
[HostName] [VARCHAR](20) NULL ,
[LoginName] [VARCHAR](20) NULL ,
[InputBuffer] [VARCHAR](1000) NULL ,
[Capture_date] [DATETIME] NOT NULL ,
[capture_day] AS ( CONVERT([VARCHAR](12), [Capture_date], ( 112 )) ) ,
[comfirm_user] [NVARCHAR](50) NULL ,
[comfirm_flag] [INT] NOT NULL
)
ON [PRIMARY] TEXTIMAGE_ON [PRIMARY];
GO
ALTER TABLE [dbo].[monitor_deadlock] ADD CONSTRAINT [DF__monitor_d__Captu__2CF2ADDF] DEFAULT (GETDATE()) FOR [Capture_date];
GO
ALTER TABLE [dbo].[monitor_deadlock] ADD CONSTRAINT [DF__monitor_d__comfi__2DE6D218] DEFAULT ((1)) FOR [comfirm_flag];
GO

b.新建读取死锁的存储过程

USE [azure_monitor];
--存放读取死锁信息的存储过程的库名
GO
IF OBJECT_ID('monitor_P_deadlock', 'P') IS NULL
EXEC( 'CREATE procedure dbo.monitor_P_deadlock AS ');
GO
/*=============================================
-- Author: jil.wen
-- Create date: 2017/04/11
-- Description: 监控数据库上死锁情况;
-- demo : exec dbo.monitor_P_deadlock
============================================= */
ALTER PROCEDURE monitor_P_deadlock
AS
BEGIN
-- DELETE FROM dbo.monitor_deadlock
-- WHERE [capture_day] = CONVERT([VARCHAR](12), GETDATE(), ( 112 ))
-- AND comfirm_flag = 1;
DECLARE @SessionName sysname;
DECLARE @Servername VARCHAR(50);
SELECT @Servername = @@SERVERNAME;
SELECT @SessionName = 'system_health';
/*
SELECT Session_Name = s.name, s.blocked_event_fire_time, s.dropped_buffer_count, s.dropped_event_count, s.pending_buffers
FROM sys.dm_xe_session_targets t
INNER JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address
WHERE target_name = 'event_file'
--*/
IF OBJECT_ID('tempdb..#Events') IS NOT NULL
BEGIN
DROP TABLE #Events;
END;
DECLARE @Target_File NVARCHAR(1000) ,
@Target_Dir NVARCHAR(1000) ,
@Target_File_WildCard NVARCHAR(1000);
SELECT @Target_File = CAST(t.target_data AS XML).value('EventFileTarget[1]/File[1]/@name',
'NVARCHAR(256)')
FROM sys.dm_xe_session_targets t
INNER JOIN sys.dm_xe_sessions s ON s.address = t.event_session_address
WHERE s.name = @SessionName
AND t.target_name = 'event_file';
SELECT @Target_Dir = LEFT(@Target_File,
LEN(@Target_File) - CHARINDEX('\',
REVERSE(@Target_File)));
SELECT @Target_File_WildCard = @Target_Dir + '\' + @SessionName
+ '_*.xel';
--Keep this as a separate table because it's called twice in the next query. You don't want this running twice.
SELECT DeadlockGraph = CAST(event_data AS XML) ,
DeadlockID = ROW_NUMBER() OVER ( ORDER BY file_name, file_offset )
INTO #Events
FROM sys.fn_xe_file_target_read_file(@Target_File_WildCard, NULL,
NULL, NULL) AS F
WHERE event_data LIKE '<event name="xml_deadlock_report%';
WITH Victims
AS ( SELECT VictimID = Deadlock.Victims.value('@id',
'varchar(50)') ,
e.DeadlockID
FROM #Events e
CROSS APPLY e.DeadlockGraph.nodes('/event/data/value/deadlock/victim-list/victimProcess')
AS Deadlock ( Victims )
),
DeadlockObjects
AS ( SELECT DISTINCT
e.DeadlockID ,
ObjectName = Deadlock.Resources.value('@objectname',
'nvarchar(256)')
FROM #Events e
CROSS APPLY e.DeadlockGraph.nodes('/event/data/value/deadlock/resource-list/*')
AS Deadlock ( Resources )
)
INSERT INTO monitor_deadlock
( ServerName ,
DataBaseName ,
DeadlockID ,
TransactionTime ,
DeadlockGraph ,
DeadlockObjects ,
Victim ,
SPID ,
ProcedureName ,
LockMode ,
Code ,
ClientApp ,
HostName ,
LoginName ,
InputBuffer
)
SELECT @Servername AS ServerName ,
DatabaseName ,
DeadlockID ,
TransactionTime ,
DeadlockGraph ,
DeadlockObjects ,
Victim ,
SPID ,
ProcedureName ,
LockMode ,
Code ,
ClientApp ,
HostName ,
LoginName ,
InputBuffer
FROM ( SELECT DatabaseName = LEFT(SUBSTRING(( SELECT
( ', '
+ o.ObjectName )
FROM
DeadlockObjects o
WHERE
o.DeadlockID = e.DeadlockID
ORDER BY o.ObjectName
FOR
XML
PATH('')
), 3, 4000),
CHARINDEX('.',
SUBSTRING(( SELECT
( ', '
+ o.ObjectName )
FROM
DeadlockObjects o
WHERE
o.DeadlockID = e.DeadlockID
ORDER BY o.ObjectName
FOR
XML
PATH('')
), 3, 4000)) - 1) ,
e.DeadlockID ,
TransactionTime = Deadlock.Process.value('@lasttranstarted',
'datetime') ,
DeadlockGraph ,
DeadlockObjects = SUBSTRING(( SELECT
( ', '
+ o.ObjectName )
FROM
DeadlockObjects o
WHERE
o.DeadlockID = e.DeadlockID
ORDER BY o.ObjectName
FOR
XML
PATH('')
), 3, 4000) ,
Victim = CASE WHEN v.VictimID IS NOT NULL
THEN 1
ELSE 0
END ,
SPID = Deadlock.Process.value('@spid',
'int') ,
ProcedureName = Deadlock.Process.value('executionStack[1]/frame[1]/@procname[1]',
'varchar(200)') ,
LockMode = Deadlock.Process.value('@lockMode',
'char(1)') ,
Code = Deadlock.Process.value('executionStack[1]/frame[1]',
'varchar(1000)') ,
ClientApp = CASE LEFT(Deadlock.Process.value('@clientapp',
'varchar(100)'),
29)
WHEN 'SQLAgent - TSQL JobStep (Job '
THEN 'SQLAgent Job: '
+ ( SELECT
name
FROM
msdb..sysjobs sj
WHERE
SUBSTRING(Deadlock.Process.value('@clientapp',
'varchar(100)'),
32, 32) = ( SUBSTRING(sys.fn_varbintohexstr(sj.job_id),
3, 100) )
) + ' - '
+ SUBSTRING(Deadlock.Process.value('@clientapp',
'varchar(100)'),
67,
LEN(Deadlock.Process.value('@clientapp',
'varchar(100)'))
- 67)
ELSE Deadlock.Process.value('@clientapp',
'varchar(100)')
END ,
HostName = Deadlock.Process.value('@hostname',
'varchar(20)') ,
LoginName = Deadlock.Process.value('@loginname',
'varchar(20)') ,
InputBuffer = Deadlock.Process.value('inputbuf[1]',
'varchar(1000)')
FROM #Events e
CROSS APPLY e.DeadlockGraph.nodes('/event/data/value/deadlock/process-list/process')
AS Deadlock ( Process )
LEFT JOIN Victims v ON v.DeadlockID = e.DeadlockID
AND v.VictimID = Deadlock.Process.value('@id',
'varchar(50)')
) X --In a subquery to make filtering easier (use column names, not XML parsing), no other reason
ORDER BY DeadlockID DESC;
END;

c.在Agent新建job调用上述【monitor_P_deadlock】存储过程

省略,详情可以参考数据库出现阻塞及时邮件预警提醒(下)

d.收集效果如下

注意事项

  • 如需使用Agent代理发送预警邮件,就要注意Agent是否正常运行;
  • 是否有调用上述脚本的数据库用户权限;
  • 数据库自带扩展事件system_health是否正常运行;

  

SQL Server 收集数据库死锁信息的更多相关文章

  1. SQL SERVER获取数据库文件信息

        MS SQL SERVER 获取当前数据库文件等信息,适用于多个版本: SELECT dbf.file_id AS FileID , dbf.name AS [FileName] , s.fi ...

  2. SQL Server里如何处理死锁 (转)

    http://www.cnblogs.com/woodytu/p/6437049.html 在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以 ...

  3. 在SQL Server里如何处理死锁

    在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以继续它们的操作.首先我想给你大致讲下SQL Server如何处理死锁.最后我会展示下SQL ...

  4. SQL Server里如何处理死锁

    在今天的文章里,我想谈下SQL Server里如何处理死锁.当2个查询彼此等待时会发生死锁,没有一个查询可以继续它们的操作.首先我想给你大致讲下SQL Server如何处理死锁.最后我会展示下SQL ...

  5. 微软ASP.NET网站部署指南(2):部署SQL Server Compact数据库

    1. 综述 对于数据库訪问,Contoso University程序要求以下的软件必须随程序一起部署.由于不属于.NET Framework: SQL Server Compact (数据库引擎) A ...

  6. 深入浅出SQL Server中的死锁

    简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...

  7. SQL Server中解决死锁

    SQL Server中解决死锁的新方法介绍 数据库操作的死锁是不可避免的,本文并不打算讨论死锁如何产生,重点在于解决死锁,通过SQL Server 2005, 现在似乎有了一种新的解决办法. 将下面的 ...

  8. SQL Server中的死锁

    简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...

  9. 深入浅出SQL Server中的死锁(实战篇)

    简介 死锁的本质是一种僵持状态,是多个主体对于资源的争用而导致的.理解死锁首先需要对死锁所涉及的相关观念有一个理解. 一些基础知识 要理解SQL Server中的死锁,更好的方式是通过类比从更大的面理 ...

随机推荐

  1. HNOI 世界树 虚树

    //virtual tree /*Huyyt*/ #include<bits/stdc++.h> #define mem(a,b) memset(a,b,sizeof(a)) #defin ...

  2. sql 实现取表中相同id时间最大的一行 利用distinct on

    数据表是这样的 select * from water_level_records m where ( select count(*) from water_level_records n where ...

  3. CentOS7 安装 RocketMQ 实践和小示例

    CentOS7 安装 RocketMQ 实践和小示例 1.通过 SSH 工具(比如 XShell)连接到 CentOS7 服务器上: 2.进入到 /usr/local 目录中: cd /usr/loc ...

  4. 决策树--CART树详解

    1.CART简介 CART是一棵二叉树,每一次分裂会产生两个子节点.CART树分为分类树和回归树. 分类树主要针对目标标量为分类变量,比如预测一个动物是否是哺乳动物. 回归树针对目标变量为连续值的情况 ...

  5. Java-DealString工具类

    import java.text.NumberFormat; import java.util.Date; import java.util.Locale; import java.util.Stri ...

  6. 9种纯CSS3人物信息卡片动态展示效果

    <!DOCTYPE html> <html lang="zh"> <head> <meta charset="UTF-8&quo ...

  7. 【Python之路】异步IO

    线程:CPU基本执行单元,可以与同属一个进程的其他线程共享资源,线程是属于进程的. 进程:资源单元,进程一般由程序.数据集.进程控制块三部分组成.一个进程默认有一个主线程, GIL:用于在进程中对所有 ...

  8. ES大批量写入提高性能的策略

    1.用bulk批量写入 你如果要往es里面灌入数据的话,那么根据你的业务场景来,如果你的业务场景可以支持让你将一批数据聚合起来,一次性写入es,那么就尽量采用bulk的方式,每次批量写个几百条这样子. ...

  9. RabbitMQ安装遇到的问题及解决记录

    提示:若是win10 请注意计算机名称不能有中文 安装Rabbit MQ 需要先安装 Erlang 这里下载版本Erlang OTP22.0 http://www.erlang.org/downloa ...

  10. Reborn & Recover

    高考总算是结束了 而我们的旅程却还在继续 现在姑且是个ACMer(暂定) 而我已经将近一年没写代码了 接下来是全新的开始 全新的学校 全新的未来 总之从现在开始 试着努力看看吧!