5. SQL Server數據庫性能監控 - 當前請求

對於在線運行的系統,當前數據庫性能監控,一般監視如下幾點:sql

(1) 是否有阻塞 (Blocking);shell

(2) 是否有等待 (Waiting),阻塞就是鎖 (Lock) 等待;數據庫

(3) 是否運行時間過長(Long running);性能優化

(4) 是否有死鎖 (Deadlock);session

sys.dm_exec_query_stats之類,等一些統計性的信息,一般不做爲實時告警內容,而是在性能優化時,做爲參考。工具

 

. 阻塞/等待/長時間運行性能

1. SQL Server 2005 及之後版本檢查測試

SELECT r.session_id
      ,r.blocking_session_id
      ,DB_Name(r.database_id) as database_name
      ,r.start_time
      ,r.total_elapsed_time
      ,r.[status]
      ,CASE WHEN r.blocking_session_id <> 0 THEN 'Blocking'
            WHEN r.blocking_session_id = 0 AND r.wait_type is not null THEN 'Waiting'
            ELSE 'Long-running'
       END as slowness_type
      ,r.percent_complete
      ,r.command
      ,r.wait_type
      ,r.wait_time
      ,r.wait_resource
      ,r.last_wait_type
      ,r.cpu_time
      ,r.reads
      ,r.writes
      ,r.logical_reads
      ,t.[text] as executing_batch
      ,SUBSTRING(t.[text],
                 r.statement_start_offset/2,
                 (CASE WHEN r.statement_end_offset = -1 
                       THEN DATALENGTH (t.[text]) --LEN(CONVERT(NVARCHAR(MAX), t.text)) * 2
                  ELSE r.statement_end_offset 
                  END - r.statement_start_offset )/2 + 1) as executing_sql
      ,bt.[text] as blocking_batch
      ,SUBSTRING(bt.[text],
                 br.statement_start_offset/2,
                 (CASE WHEN br.statement_end_offset = -1 
                       THEN DATALENGTH (bt.[text]) --LEN(CONVERT(NVARCHAR(MAX), bt.text)) * 2
                  ELSE br.statement_end_offset 
                  END - br.statement_start_offset )/2 + 1) as blocking_sql
     --,p.query_plan
  FROM sys.dm_exec_requests r
 CROSS APPLY sys.dm_exec_sql_text(r.sql_handle) as t
 CROSS APPLY sys.dm_exec_query_plan(r.plan_handle) as p
  LEFT JOIN sys.dm_exec_requests br
    ON r.blocking_session_id = br.session_id
 OUTER APPLY sys.dm_exec_sql_text(br.session_id) as bt
 WHERE r.session_id > 50 and r.session_id <> @@SPID
   AND r.total_elapsed_time > 30 * 60 * 1000
 ORDER BY r.total_elapsed_time DESC;

以上腳本返回運行超過30分鐘的語句,須要注意的是:優化

(1) 若是返回執行計劃,會讓以上腳本變慢不少,能夠不返回,在收到告警後檢查語句時,再去查看執行計劃;spa

(2) 顯示TEXT,好比: xp_cmdshell這樣的語句,start_offset, end_offset都爲0,截取的 text是空白,只有看TEXT才知道是什麼語句;還有就是有時須要知道這個請求來自哪一個batch或者存儲過程;

(3) 有時顯示TEXT還不夠,還以xp_cmdshell爲例,須要dbcc inputbuffer才能看到完整的sql語句;另外已運行結束但尚未commit/rollback的事務,在requests中已經沒有了,也須要借用dbcc inputbuffer來查看sql 語句;

dbcc inputbuffer(@@SPID)

(4) SQL Agent做業,在這裏會被一併檢查,也能夠經過msdb..sysjobactivity另行檢查;

select b.name, * 
  from msdb..sysjobactivity a
 inner join msdb.dbo.sysjobs b
    on a.job_id = b.job_id
 where b.name like '%backup%'

 

2. SQL Server 2000沿用過來的方法

select p.dbid, p.spid, p.blocked, p.waittime/1000.0/60.0 as wait_minutes, 
       ISNULL(DATEDIFF(MI, p.last_batch, GETDATE()), 0) elapsed_minutes, 
       p.last_batch, p.status, p.program_name, 
       (select [text] FROM ::fn_get_sql(p.sql_handle)) sql_text 
  from master..sysprocesses p
 where spid > 50 and spid <> @@SPID
   AND (status <> 'sleeping' AND ISNULL(DATEDIFF(MI, p.last_batch, GETDATE()), 0) > 30)

以上腳本返回運行超過30分鐘的語句,須要注意的是:

sysprocesses中把connection/session/request信息三者合一,其中沒有請求開始的具體時間,經過last_batch監視運行時長並不許確。測試以下:

(1) 經過ISQL鏈接到SQL Server,若是當前鏈接沒發起過任何請求,last_batch的時間爲 1900-01-01 00:00:00,在此鏈接上發起請求時,經過last_batch計算當前請求運行時長不許確;

(2) 在SQL Analyzer/SSMS中新建查詢窗口,未發起任何查詢時,last_batch與login_time同樣,而非1900-01-01 00:00:00,經過last_batch計算當前請求運行時長不許確;或者當前窗口發起的請求已結束,但窗口/鏈接未關閉,則在此鏈接上再次發起請求,last_batch爲上次請求結束的時間,經過last_batch計算當前請求運行時長也不許確;

(3) SQL Agent做業運行結束後,會把在sysprocesses的鏈接關閉,下次運行時從新創建鏈接,新建鏈接中last_batch等於login_time,經過last_batch計算做業運行時長準確;

 

做爲一個老的方法,估且再也不去深究,不過用sysprocesses來監視阻塞/等待仍是沒有問題的,另外做業的運行時長也是能夠監視的,腳本改動後以下:

select p.dbid, p.spid, p.blocked, p.waittime/1000.0/60.0 as wait_minutes, 
       ISNULL(DATEDIFF(MI, p.last_batch, GETDATE()), 0) elapsed_minutes, 
       p.last_batch, p.status, p.program_name, 
       (select [text] FROM ::fn_get_sql(p.sql_handle)) sql_text 
  from master..sysprocesses p
 where spid > 50 and spid <> @@SPID
   and (
       (p.program_name like 'SQLAgent - TSQL JobStep (Job %' AND ISNULL(DATEDIFF(MI, p.last_batch, GETDATE()), 0) > 30)
       or 
       (p.blocked <> 0 and p.waittime/1000.0/60.0 > 30)
       )

這樣一來,只剩下未被阻塞但長時間運行的sql請求未被監視到。若是必定要全面監視的話,能夠選擇開啓跟蹤,進而分析跟蹤文件。

 

. 死鎖

死鎖的監控能夠經過監視SQL Server的ERRORLOG來實現,不過須要事先打開死鎖的跟蹤標記。腳本以下:

--sql server 2000
dbcc traceon(1204,-1)

--sql server 2005 +
dbcc traceon(1222,-1)

這樣發生死鎖時,死鎖詳細信息就會被寫入ERRORLOG,檢查deadlock或者victim關鍵字便可進行監控。

 

小結

各個語句的運行時長/基線並不同,一般很差設置統一的閥值,有時會借用第三方工具針對不一樣的請求設置不一樣的時長閥值並告警,因此在數據庫這層大多告警阻塞便可,大體步驟以下 :

(1) 部署數據庫郵件;

(2) 部署做業:定時檢查阻塞,發郵件告警。

相關文章
相關標籤/搜索