又近放假,發現自己近來有一種放假前做總結的習慣。剛好這兩天一個系統總是會出現陣發性的性能問題。經過分析,發現這系統是之前趕出來的系統,什么是趕出來的系統,我想多數人都明白的,很多時候都無可奈何,不多說。現在出問題了,用戶罵聲一遍。
問題的原因在分析的過程中得到了證實,當時做這個系統時,在沒有考慮性能的情況下做出了功能。但是誰又能保證呢?
所以要做些工作:
1. 找出執行時間長的SQL
2. 優化
本文只講找性能差的SQL,不講優化,因為優化要對特定的內容。有機會再做一下優化的經驗總結。
好,看看要怎樣找問題SQL,整個應用中所有的SQL都檢查一下是不可能的,就算你真的那么做了,也不可能就沒事了,有的看起來沒有問題的句子,在生產環境中實際是有問題的。所以,我們就在生產運行過程中,在真實的使用中去想辦法找出來。
從兩個問題來入手:
問題一,你知道現在正在執行些什么SQL語句?
問題二,正在執行的SQL語句各運行了多長時間了?
在MS SQL中有一個系統視圖sys.sysprocesses,它有以下的內容:
所有字段列表:spid kpid blocked waittype waittime lastwaittype waitresource dbid uid cpu physical_io memusage
login_time last_batch ecid open_tran status sid hostname program_name hostprocess cmd
nt_domain nt_username net_address net_library loginame context_info sql_handle
stmt_start stmt_end request_id
以下有圖,但是我只想關心其中的一部分內容就夠了,我要這個視圖中的spid,數據庫名、用戶名、電腦名等就可以了,具體就看你自己的需要了。
好多的內容在系統視圖sys.sysprocesses中,多數在這里用不到,但都是很有用的。不過,要找正在執行的語句,這里就沒有,我們要在另兩個系統視圖中找,一個是sys.dm_exec_connections,如下:
使用以下方式就可以得到正在執行的SQL
1: SELECT c.session_id,t.text
2: FROM sys.dm_exec_connections c
3: CROSS APPLY sys.dm_exec_sql_text (c.most_recent_sql_handle) t
第一個問題解決,正在執行什么SQL已可以知道,下邊,我們就要從這些SQL中找出執行時間有問題的,比如執行時間大於1分鍾的語句,那就要用上邊說到的三個視圖一起找,可以用以下的方式,你看到有很多的條件可以用,例如你要看或不看某個程序的、某個用戶的、某台電腦的SQL,都是可以的。按需靈活設定。有個要注意的是,status要取活動的。
這樣第二個問題也已解決,問題SQL已有辦法得到,但是不可能時時去執行這個語句,因為我們會有幾個人看各自己的部分,並且不可能什么語句都會剛好在有問題時讓你發現,我想了一個辦法,就是讓數據庫定時自動找出這些語句,通過DBMail發郵件出來。
通過試驗,已初步完成了功能,完整的代碼貼上,但是這只是說明我的方法的樣例,在實際的應用中要考慮的還有很多問題,在這里就不說。
1: DECLARE @html NVARCHAR(MAX);
2:
3: with tb
4: as
5: (
6: SELECT c.session_id,t.text
7: FROM sys.dm_exec_connections c
8: CROSS APPLY sys.dm_exec_sql_text (c.most_recent_sql_handle) t
9: )
10: select distinct x.spid,DB_NAME(x.dbid) as dbname,x.last_batch,x.hostname,x.program_name,x.nt_domain,x.nt_username,tb.text
11: into #T
12: from sys.sysprocesses x with (nolock)
13: inner join tb on x.spid=tb.session_id
14: where x.last_batch<dateadd(mi,-15,getdate())
15: and x.program_name<>'Report Server'
16: and nt_domain<>'NT AUTHORITY'
17: and x.status<>'sleeping'
18: and x.hostname<>'HZCSRPTSRV'
19:
20: if exists(select top 1 * from #T)
21: begin
22: SET @html = '<style type=''text/css''>.header {text-align:center;font-weight:bold;white-space:nowrap;color:#7f7e82;} .cell_text {vertical-align:top;text-align:left;color:#333333;} .cell_num {vertical-align:top;text-align:right;color:#333333;}</style>' ;
23:
24: SET @html = @html + CAST(( SELECT 3 [@cellpadding],0 [@cellspacing],'font-family:verdana;font-size:10px;' [@style],1 [@border],
25: ( SELECT [@class] = 'header', 'spid' [text()] FOR XML PATH('th'), TYPE) tr,
26: ( SELECT [@class] = 'header', 'dbname' [text()] FOR XML PATH('th'), TYPE) tr,
27: ( SELECT [@class] = 'header', 'last_batch' [text()] FOR XML PATH('th'), TYPE) tr,
28: ( SELECT [@class] = 'header', 'hostname' [text()] FOR XML PATH('th'), TYPE) tr,
29: ( SELECT [@class] = 'header', 'program_name' [text()] FOR XML PATH('th'), TYPE) tr,
30: ( SELECT [@class] = 'header', 'nt_domain' [text()] FOR XML PATH('th'), TYPE) tr,
31: ( SELECT [@class] = 'header', 'nt_username' [text()] FOR XML PATH('th'), TYPE) tr,
32: ( SELECT [@class] = 'header', 'text' [text()] FOR XML PATH('th'), TYPE) tr,
33: ( SELECT
34: ( SELECT [@class] = 'cell_text', spid [text()] FOR XML PATH('td'), TYPE ),
35: ( SELECT [@class] = 'cell_text', dbname [text()] FOR XML PATH('td'), TYPE ),
36: ( SELECT [@class] = 'cell_text', last_batch [text()] FOR XML PATH('td'), TYPE ),
37: ( SELECT [@class] = 'cell_text', hostname [text()] FOR XML PATH('td'), TYPE ),
38: ( SELECT [@class] = 'cell_text', program_name [text()] FOR XML PATH('td'), TYPE ),
39: ( SELECT [@class] = 'cell_text', nt_domain [text()] FOR XML PATH('td'), TYPE ),
40: ( SELECT [@class] = 'cell_text', nt_username [text()] FOR XML PATH('td'), TYPE ),
41: ( SELECT [@class] = 'cell_text', text [text()] FOR XML PATH('td'), TYPE )
42: FROM (
43: select spid,dbname,last_batch,hostname,program_name,nt_domain,nt_username,text
44: from #T
45: ) data
46: FOR XML PATH('tr'), TYPE
47: )
48: FOR XML PATH('table'), TYPE
49: ) AS VARCHAR(MAX));
50: drop table #T;
51: --Send Email
52: EXEC msdb.dbo.sp_send_dbmail
53: @profile_name = 'DBMAIL'
54: ,@recipients = 'DarrenXie@QQ.com'
55: ,@copy_recipients = 'QQQQQ@QQ.com'
56: ,@subject = 'Camstar HZCSRPTSRV long runtime process'
57: ,@body = @html
58: ,@importance ='High'
59: ,@body_format= 'HTML'
60: end
61: else
62: begin
63: drop table #T;
64: end
你用以上代碼建立SP,再建立JOB定時執行就可以了,一有問題就會發出像以下的內容郵件,你就可以知道相關的內容了,接着你就去做分析優化吧!
最后說明一下,我們可以看到這個方式是基於執行時間的,B/S的程序都差不多是連接數據庫執行后就斷開的,如果有應用是持久連接的,那就不准確了。
這里只是總結個方法,各自參考參考。
原創,轉載請注明出處:http://www.cnblogs.com/YIYUMENG