背景

我们在数据库出现阻塞及时邮件预警提醒中监控了数据库的阻塞情况,为了更好的维护数据库,特别是提升终端客户用户体验,我们要尽量避免在数据库中出现死锁的情况。我们知道收集死锁可以开启跟踪标志如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. 数据库——Oracle(5)

    1 唯一约束: 1)修改表的时候设置唯一约束 alter table 表名 add constraint 约束名 unique(列名1,列名2,列名3...) create table worker8 ...

  2. Java语言基础(12)

    1 构造方法重载 在一个类内部,编写多个构造方法,创建对象的时候,根据需求的不同,调用不同的构造方法创建对象,实现不同的初始化. 案例:Demo1 public class Demo1 { publi ...

  3. 锁&lock与latch

    锁是数据库系统区别于文件系统的一个关键特性.锁机制用于管理对共享资源的并发访问.Innodb不仅仅使用行锁,也会在数据库内部其它地方使用锁,从而允许对多种不同资源提供并发访问.如:操作缓冲池中的LRU ...

  4. 图片框住一个小视频 谈css padding百分比自适应

    今天市场提出活动页,活动页有一块内容是在一个手机背景图框里播放视频,网页是适配的,设计师只给我一张带有手机壳的背景图. 如果用JS画应该也是可以的,但一个简单的活动页没必要,快速实现用背景图调CSS最 ...

  5. 如何阻止<a>标签默认行为和表单提交

    阻止<a>标签默认行为 方式一 (通过return false) <!DOCTYPE html> <html> <head> <meta char ...

  6. 制作中文字符集zh_CN.utf8的centos7系统

    以下是Dockerfile文件 ===================================== FROM centos:7ENV LANG=zh_CN.UTF-8 \ LANGUAGE=z ...

  7. CentOS下安装libjpeg库及编译GD库

    GD库明明安装了,可处理图片的时候还是报错 Fatal error: Call to undefined function imagecreatefromjpeg() .PHP安装后,默认的gd库不支 ...

  8. java常见问题 ——运行报错1

    错误1 打印乱码 相关代码 response.getWriter().print(tbItem.toString()); response.setContentType("text/html ...

  9. java上传视频文件

    需求:项目要支持大文件上传功能,经过讨论,初步将文件上传大小控制在500M内,因此自己需要在项目中进行文件上传部分的调整和配置,自己将大小都以501M来进行限制. 第一步: 前端修改 由于项目使用的是 ...

  10. docker 卸载与安装

    卸载 Docker自17.03版本开始分为两个版本Docker CE和Docker EE: Docker CE:Docker Community Edition,即Docker社区版 Docker E ...