公司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)= '123@123.cn'; --收件人,以";"分隔 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,還是采用之前的兩個會話,查看報警。
郵箱收到報警:
結果表已經插入數據: