1. 程式人生 > >SQL Server 收集數據庫死鎖信息

SQL Server 收集數據庫死鎖信息

死鎖 eas declare 效果 cnblogs lar card bst ever

背景

我們在數據庫出現阻塞及時郵件預警提醒中監控了數據庫的阻塞情況,為了更好的維護數據庫,特別是提升終端客戶用戶體驗,我們要盡量避免在數據庫中出現死鎖的情況。我們知道收集死鎖可以開啟跟蹤標誌如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][email protected]
/* */, ‘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([email protected], ‘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([email protected], ‘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([email protected], ‘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([email protected], ‘int‘) , ProcedureName = Deadlock.Process.value(‘executionStack[1]/frame[1][email protected][1]‘, ‘varchar(200)‘) , LockMode = Deadlock.Process.value([email protected], ‘char(1)‘) , Code = Deadlock.Process.value(‘executionStack[1]/frame[1]‘, ‘varchar(1000)‘) , ClientApp = CASE LEFT(Deadlock.Process.value([email protected], ‘varchar(100)‘), 29) WHEN ‘SQLAgent - TSQL JobStep (Job ‘ THEN ‘SQLAgent Job: ‘ + ( SELECT name FROM msdb..sysjobs sj WHERE SUBSTRING(Deadlock.Process.value([email protected], ‘varchar(100)‘), 32, 32) = ( SUBSTRING(sys.fn_varbintohexstr(sj.job_id), 3, 100) ) ) + ‘ - ‘ + SUBSTRING(Deadlock.Process.value([email protected], ‘varchar(100)‘), 67, LEN(Deadlock.Process.value([email protected], ‘varchar(100)‘)) - 67) ELSE Deadlock.Process.value([email protected], ‘varchar(100)‘) END , HostName = Deadlock.Process.value([email protected], ‘varchar(20)‘) , LoginName = Deadlock.Process.value([email protected], ‘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([email protected], ‘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 收集數據庫死鎖信息