想個法子找出性能差的SQL


又近放假,發現自己近來有一種放假前做總結的習慣。剛好這兩天一個系統總是會出現陣發性的性能問題。經過分析,發現這系統是之前趕出來的系統,什么是趕出來的系統,我想多數人都明白的,很多時候都無可奈何,不多說。現在出問題了,用戶罵聲一遍。

問題的原因在分析的過程中得到了證實,當時做這個系統時,在沒有考慮性能的情況下做出了功能。但是誰又能保證呢?

所以要做些工作:

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,數據庫名、用戶名、電腦名等就可以了,具體就看你自己的需要了。

image

 

image

 

image

 

好多的內容在系統視圖sys.sysprocesses中,多數在這里用不到,但都是很有用的。不過,要找正在執行的語句,這里就沒有,我們要在另兩個系統視圖中找,一個是sys.dm_exec_connections,如下:

 

image

 

image

使用以下方式就可以得到正在執行的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
 

image

第一個問題解決,正在執行什么SQL已可以知道,下邊,我們就要從這些SQL中找出執行時間有問題的,比如執行時間大於1分鍾的語句,那就要用上邊說到的三個視圖一起找,可以用以下的方式,你看到有很多的條件可以用,例如你要看或不看某個程序的、某個用戶的、某台電腦的SQL,都是可以的。按需靈活設定。有個要注意的是,status要取活動的。

image

這樣第二個問題也已解決,問題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定時執行就可以了,一有問題就會發出像以下的內容郵件,你就可以知道相關的內容了,接着你就去做分析優化吧!

image

 

image

 

最后說明一下,我們可以看到這個方式是基於執行時間的,B/S的程序都差不多是連接數據庫執行后就斷開的,如果有應用是持久連接的,那就不准確了。

這里只是總結個方法,各自參考參考。

 

原創,轉載請注明出處:http://www.cnblogs.com/YIYUMENG


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM