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

原文地址:https://www.bbsmax.com/A/QV5ZxZZezy/

 

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

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

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

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

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

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

  1. --查询阻塞
  2. SELECT R.session_id AS BlockedSessionID ,
  3. S.session_id AS BlockingSessionID ,
  4. Q1.text AS BlockedSession_TSQL ,
  5. Q2.text AS BlockingSession_TSQL ,
  6. C1.most_recent_sql_handle AS BlockedSession_SQLHandle ,
  7. C2.most_recent_sql_handle AS BlockingSession_SQLHandle ,
  8. S.original_login_name AS BlockingSession_LoginName ,
  9. S.program_name AS BlockingSession_ApplicationName ,
  10. S.host_name AS BlockingSession_HostName
  11. FROM sys.dm_exec_requests AS R
  12. INNER JOIN sys.dm_exec_sessions AS S ON R.blocking_session_id = S.session_id
  13. INNER JOIN sys.dm_exec_connections AS C1 ON R.session_id = C1.most_recent_session_id
  14. INNER JOIN sys.dm_exec_connections AS C2 ON S.session_id = C2.most_recent_session_id
  15. CROSS APPLY sys.dm_exec_sql_text(C1.most_recent_sql_handle) AS Q1
  16. CROSS APPLY sys.dm_exec_sql_text(C2.most_recent_sql_handle) AS Q2

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

会话2执行sql后产生阻塞

用该sql查询的结果:

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

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

  1. USE etcp_alert
  2. GO
  3. CREATE TABLE [dbo].[BlockLog]
  4. (
  5. Id , )
  6. NOT NULL
  7. PRIMARY KEY ,
  8. [BlockingSessesionId] [smallint] NULL ,
  9. ) NULL ,
  10. ) NULL ,
  11. ) NULL ,
  12. [DatabaseName] [sysname] NOT NULL ,
  13. ) NULL ,
  14. [BlockingStartTime] [datetime] NOT NULL ,
  15. [WaitDuration] [bigint] NULL ,
  16. [BlockedSessionId] [int] NULL ,
  17. [BlockedSQLText] [nvarchar](MAX) NULL ,
  18. [BlockingSQLText] [nvarchar](MAX) NULL ,
  19. [dt] [datetime] NOT NULL
  20. )
  21. ON [PRIMARY]
  22. GO

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

  

  

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

  1. SET NOCOUNT ON;
  2. DECLARE @dt DATETIME= GETDATE();
  3. -- 阻塞时间
  4. DECLARE @HtmlContent NVARCHAR(MAX);
  5. --邮件发送的阻塞日志(表格形式)
  6.  
  7. IF OBJECT_ID('tempdb.dbo.#BlockLog') IS NOT NULL
  8. DROP TABLE #BlockLog;
  9. --将当前日志记录插入临时表
  10. BEGIN
  11. SELECT wt.blocking_session_id AS BlockingSessesionId ,
  12. sp.program_name AS ProgramName ,
  13. COALESCE(sp.LOGINAME, sp.nt_username) AS HostName ,
  14. ec1.client_net_address AS ClientIpAddress ,
  15. db.name AS DatabaseName ,
  16. wt.wait_type AS WaitType ,
  17. ec1.connect_time AS BlockingStartTime ,
  18. wt.WAIT_DURATION_MS AS WaitDuration ,
  19. ec1.session_id AS BlockedSessionId ,
  20. h1.TEXT AS BlockedSQLText ,
  21. h2.TEXT AS BlockingSQLText ,
  22. @dt dt
  23. INTO #BlockLog
  24. FROM sys.dm_tran_locks AS tl
  25. INNER JOIN sys.databases db ON db.database_id = tl.resource_database_id
  26. INNER JOIN sys.dm_os_waiting_tasks AS wt ON tl.lock_owner_address = wt.resource_address
  27. INNER JOIN sys.dm_exec_connections ec1 ON ec1.session_id = tl.request_session_id
  28. INNER JOIN sys.dm_exec_connections ec2 ON ec2.session_id = wt.blocking_session_id
  29. LEFT OUTER JOIN master.dbo.sysprocesses sp ON SP.spid = wt.blocking_session_id
  30. CROSS APPLY sys.dm_exec_sql_text(ec1.most_recent_sql_handle) AS h1
  31. CROSS APPLY sys.dm_exec_sql_text(ec2.most_recent_sql_handle) AS h2;
  32. --将临时表数据插入日志表
  33. INSERT INTO etcp_alert.dbo.BlockLog
  34. ( BlockingSessesionId ,
  35. ProgramName ,
  36. HostName ,
  37. ClientIpAddress ,
  38. DatabaseName ,
  39. WaitType ,
  40. BlockingStartTime ,
  41. WaitDuration ,
  42. BlockedSessionId ,
  43. BlockedSQLText ,
  44. BlockingSQLText ,
  45. dt
  46. )
  47. SELECT BlockingSessesionId ,
  48. ProgramName ,
  49. HostName ,
  50. ClientIpAddress ,
  51. DatabaseName ,
  52. WaitType ,
  53. BlockingStartTime ,
  54. WaitDuration ,
  55. BlockedSessionId ,
  56. BlockedSQLText ,
  57. BlockingSQLText ,
  58. dt
  59. FROM #BlockLog;
  60. END;
  61. --以html表格方式发送邮件,如果不发送邮件,则删除以下代码
  62. BEGIN
  63. SET @HtmlContent = N'<head>'
  64. + N'<style type="text/css">h2, body {font-family: Arial, verdana;} table{font-size:11px; border-collapse:collapse;} td{ border:1px solid black; padding:3px;} th{background-color:#99CCFF;}</style>'
  65. + N'<table border="1">' + N'<tr>
  66. <th>BlockingSessesionId</th>
  67. <th>ProgramName</th>
  68. <th>HostName</th>
  69. <th>ClientIpAddress</th>
  70. <th>DatabaseName</th>
  71. <th>WaitType</th>
  72. <th>BlockingStartTime</th>
  73. <th>WaitDuration</th>
  74. <th>BlockedSessionId</th>
  75. <th>BlockedSQLText</th>
  76. <th>BlockingSQLText</th>
  77. <th>dt</th>
  78. </tr>' + CAST(( SELECT BlockingSessesionId AS TD ,
  79. '' ,
  80. ProgramName AS TD ,
  81. '' ,
  82. HostName AS TD ,
  83. '' ,
  84. ClientIpAddress AS TD ,
  85. '' ,
  86. DatabaseName AS TD ,
  87. '' ,
  88. WaitType AS TD ,
  89. '' ,
  90. BlockingStartTime AS TD ,
  91. '' ,
  92. WaitDuration AS TD ,
  93. '' ,
  94. BlockedSessionId AS TD ,
  95. '' ,
  96. BlockedSQLText AS TD ,
  97. '' ,
  98. BlockingSQLText AS TD ,
  99. '' ,
  100. dt AS Td ,
  101. ''
  102. FROM #BlockLog
  103. FOR
  104. XML PATH('tr') ,
  105. TYPE
  106. ) AS NVARCHAR(MAX)) + N'</table>';
  107. IF @HtmlContent IS NOT NULL
  108. BEGIN
  109. )= 'db_mail'; --邮箱公用账户名称
  110. )= '123@123.cn'; --收件人,以";"分隔
  111. )= '数据库阻塞警报'; --主题
  112. EXEC msdb.dbo.sp_send_dbmail @profile_name = @ProfileName,
  113. @recipients = @RecipientsLst, @subject = @subject,
  114. @body = @HtmlContent, @body_format = 'HTML';
  115. END;
  116. begin
  117. DROP TABLE #BlockLog;
  118. END;
  119. END;

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

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

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

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

四、测试

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

邮箱收到报警:

结果表已经插入数据:

posted @ 2019-07-05 16:11  mimo0  阅读(820)  评论(0)    收藏  举报