原文出處:http://blog.csdn.net/dba_huangzj/article/details/7623926
SQL查詢性能的好壞直接影響到整個數據庫的價值,對此,必須鄭重對待。sql
SQL Server提供了多種工具,下面作一個簡單的介紹:數據庫
1、SQL Profiler工具緩存
SQL Profiler可用於:安全
l 圖形化監視SQLServer查詢;服務器
l 在後臺收集查詢信息;函數
l 分析性能;工具
l 診斷像死鎖這樣的問題;性能
l 調試Transact-SQL(T-SQL)語句;學習
l 模擬重放SQLServer活動大數據
注意:定義一個跟蹤最有效的方法是經過系統存儲過程,可是學習的起點仍是經過GUI。
1.一、 Profiler跟蹤:
建議使用標準模版
1.二、 事件:
一個事件表現SQLServer中執行的各類活動。能夠簡單分類爲:事件類、遊標事件、鎖事件、存儲過程事件和T-SQL事件。
對於性能分析,主要關心如下部分:
l SQL活動涉及哪一類的CPU使用?
l 使用了多少內存?
l 涉及多少I/O操做?
l SQL活動執行了多長時間?
l 特定的查詢執行的頻率多高?
l 查詢面對哪類錯誤和警告?
跟蹤查詢結束的事件:
事件類 |
事件 |
描述 |
Stored Procedures |
RPC:Completed |
RPC完成事件 |
SP:Completed |
存儲過程完成事件 |
|
SP:StmtCompleted |
在存儲過程當中一條SQL語句完成事件 |
|
TSQL |
SQL:BatchCompleted |
T-SQL批完成事件 |
SQL:StmtCompleted |
一條T-SQL語句完成事件 |
RPC事件表示存儲過程使用遠程過程調用(RPC)機制經過OLEDB命令執行。若是一個數據庫應用程序使用T-SQL EXECUTE語句執行一個存儲過程,那麼會被轉化爲一個SQL批而不是一個RPC,RPC一般比EXECUTE請求快,由於它們繞過了SQLServer中的許多語句解析和參數處理。
T-SQL批是一組被一塊兒提交到SQLServer的SQL查詢,以GO結束。GO不是一條T-SQL語句,而是有Sqlcmd使用程序和Management Studio識別。象徵着批的結束。T-SQL批由一條或多條T-SQL語句組成。語句或T-SQL語句在存儲過程(如下簡稱SP)中也是獨立和離散的。用SP:StmtCompleted或SQL:StmtCompleted事件捕獲單獨的語句可能代價很高。收集時要很是謹慎,特別在生產環境上。
跟蹤查詢性能的事件:
事件類 |
事件 |
描述 |
Security Audit(安全審計) |
Audit Login(登陸審計) |
記錄用戶鏈接到SQL Server或斷開鏈接時數據庫的鏈接 |
Audit Logou(註銷審計) |
||
Seesions(會話) |
ExistingConnection(現有鏈接) |
表示全部在跟蹤開始以前鏈接到SQLServer的用戶 |
Cursors(遊標) |
CursorImplicitConversion(遊標隱含轉換) |
代表建立的遊標類型與所請求的類型不一樣。 |
Errors and Warnings(錯誤和警告) |
Attention(注意) |
表示因爲客戶撤銷查詢或者數據庫鏈接破壞引發的請求中斷 |
Exception(異常) |
代表SQLServer中發生了異常 |
|
Execution Warnings(執行警告) |
代表在查詢或SP執行過程當中出現了警告 |
|
Hash Warning(hash警告) |
代表hash操做中發生了錯誤 |
|
Missing Column Statistics(列統計丟失) |
代表優化器要求的肯定處理策略用的列統計丟失。 |
|
Missing Join Predicate(鏈接斷言丟失) |
代表查詢在兩表之間沒有鏈接斷言狀況下執行。 |
|
Sort Warnings(排序警告) |
代表像select這樣的查詢中執行的排序操做沒有合適的內存。 |
|
Locks(鎖) |
Lock: Deadlock(死鎖) |
標誌着死鎖的出現 |
Lock: Deadlock Chain(死鎖鏈) |
顯示產生死鎖的查詢鏈條 |
|
Lock: Timeout(鎖超時) |
表示鎖已經超過了其超時參數,該參數由SET LOCK_TIMEOUT timeout_period(MS)命令設置 |
|
Stored Procedures(存儲過程) |
SP:Recompile(重編譯) |
代表用於一個存儲過程的執行計劃必須重編譯,緣由是執行計劃不存在,強制的重編譯,或者現有的執行計劃不能重用。 |
SP:Starting(開始) SP:StmtStarting(語句開始) |
分別表示一個SP:StmtStarting存儲過程和存儲過程當中的一條SQL語句的開始。它們對於識別開始但由於一個操做致使Attention事件而未能結束的查詢頗有用。 |
|
Transactions(事務) |
SQLTransaction(SQL事務) |
提供數據庫事務的信息,包括事務開始/結束的時間、事務持續時間的信息。 |
1.三、 數據列:事件的特性。如事件的類、用於該事件的SQL語句、鎖資源開銷及事件來源。
數據列 |
描述 |
EventClass(事件類) |
事件類型,如SQL:StatementCompleted |
TextData |
事件所用的SQL語句 |
CPU |
事件的CPU開銷(ms) |
Reads |
爲一個事件所執行的邏輯讀操做數量。 |
Writes |
一個事件所執行的邏輯寫操做數量。 |
Duration |
事件的執行事件(ms) |
SPID |
該事件的進程ID |
StratTime |
事件開始的事件 |
邏輯讀、寫由內存中的8KB頁面活動組成,可能須要0或者多個物理I/O。找到物理I/O操做數,使用系統監視工具。
2、跟蹤的自動化
注意:SQL Profiler對性能存在負面影響,如非必要不要在生產環境長期使用。
1. 使用GUI捕捉跟蹤:
可使用兩種方法建立腳本化的跟蹤——手工或GUI:
可使用Profiler的導出功能導出腳本。
2. 使用存儲過程捕捉跟蹤:
l Sp_trace_create:建立一個跟蹤定義。
l Sp_trace_setevent:添加事件和事件列到跟蹤中。
l Sp_trace_setfilter:將過濾器應用到跟蹤。
可使用內建函數:fn_trace_getinfo肯定正在運行的跟蹤:
SELECT * FROM ::fn_trace_getinfo(default);
可使用:sp_trace_setstatus中止特定的跟蹤:
EXEC sp_trace_setstatus 1,0
關閉跟蹤後,必須刪除:
EXEC sp_trace_setstatus 1,2
能夠從新執行fn_trace_getinfo函數確認是否已經關閉。
3、結合跟蹤和性能監視器輸出
能夠結合SQL Profiler和性能監視器來分析性能,此處很少說
4、SQL Profiler建議
使用SQL Profiler時,要考慮如下幾點:
l 限制事件和數據列的數量;
l 拋棄用於性能分析的啓動事件;
l 限制跟蹤輸出大小;
l 避免聯機數據列排序;
l 遠程運行Profiler
一、 限制事件和數據列:
捕捉像鎖和執行計劃這樣的事件時應該當心進行,由於輸出會變得很是大並下降SQL Server性能。
二、 丟棄性能分析所用的啓動事件:
像SP:StmtStarting這樣的啓動事件不提供分析信息,由於只有事件完成才能計算I/O量、CPU負載和查詢的持續時間。
使用捕捉啓動事件的時機是:預期某些SQL查詢由於錯誤而不能結束執行,或者頻繁發現Attention事件按的時候捕捉。由於Attention事件通常表示用戶中途撤銷了查詢或者查詢超時,可能由於查詢運行了太長時間。
三、 限制跟蹤輸出大小:
在Edit Filter(編輯過濾器)對話框中作如下設置:
l Duration-Greater than or equal:2(持續事件>=2):持續事件等於0或1ms的查詢不能進一步優化。
l Reads-Greater than or equal:2(讀操做數量>=2):邏輯讀數量等於0或1的查詢不能進一步優化。
四、 避免在線數據列排序:
(1)、捕捉跟蹤,不作任何排序或分組。
(2)、保存跟蹤輸出到一個跟蹤文件。
(3)、打開跟蹤文件並按照須要排序。
五、 遠程運行Profiler:
使用系統存儲過程比使用GUI對性能方面有好處。
六、 限制使用某些事件:在已經遇到壓力的系統上,不要使用Showplan XML事件
5、沒有Profiler狀況下的查詢性能度量
對於須要當即捕捉系統,使用DMV:sys.dm_exec_query_stats比Profiler有效,若是須要查詢運行機器單獨開銷的歷史記錄,跟蹤還是更好的工具。
sys.dm_exec_query_stats:獲取服務器上查詢計劃統計的信息:
列 |
描述 |
Plan_handle |
引用執行計劃的指針 |
Creation_time |
計劃建立的時間 |
Last_execution time |
查詢最後一次使用計劃的時間 |
Execution_count |
計劃已經使用的次數 |
Total_worker_time |
從建立起計劃使用的CPU時間 |
Total_logical_reads |
從建立起計劃使用的讀操做數量 |
Total_logical_writes |
從建立起計劃使用的寫操做數量 |
Query_hash |
可用於識別有相似邏輯的查詢的一個二進制hash |
Query_plan_hash |
可用於識別有類似邏輯的計劃的一個二進制hash |
爲了過濾信息,須要關聯其餘DMF。如sys.dm_exec_sql_text來查看查詢文本。
Sys.dm_query_plan顯示查詢的執行計劃。從而限制沒必要要的返回信息。
6、開銷較大的查詢
對於收集結果,應該分析兩部分:
l 致使大量系統資源壓力的查詢;
l 速度下降最嚴重的查詢
一、 識別開銷較大的查詢:
對於返回的跟蹤數據,CPU和Reads列顯示了查詢開銷所在。在執行讀操做時,內存頁面必須在操做查詢中被備份,在第一次數據訪問期間寫入,並在內存瓶頸時被移到磁盤。過多頁面CPU還會增長管理頁面的負擔。
致使大量邏輯讀的查詢一般在相應的大數據集上獲得鎖。即便讀,也須要在全部數據上的共享鎖。阻塞了其餘請求修改的查詢。但不阻塞讀數據的查詢。若是查詢好久,那麼會持續阻塞其餘查詢,被阻塞的查詢進一步阻塞其餘查詢,引發數據中的阻塞鏈。
結論,識別開銷大的查詢並首先優化它們從而達到如下效果:
l 增進開銷較大的查詢自己的性能;
l 下降系統資源上的整體壓力;
l 減小數據庫阻塞;
開銷大的查詢有兩類:
l 單次執行:查詢一次開銷較大
l 屢次執行:查詢自己不大,可是重複執行致使系統資源上的壓力。
1. 單次執行開銷較大的查詢:
可使用SQL Profiler,或者查詢sys.dm_exec_query_stats來識別開銷大的查詢。
(1)、捕捉表示典型工做負載的Profiler跟蹤。
(2)、將跟蹤輸出保存到一個跟蹤文件。
(3)、打開跟蹤文件進行分析。
(4)、打開跟蹤的Properties(屬性)窗口,單擊Event Selection(事件選擇)選項卡。
(5)、單機按鈕打開Organize Columns(組織列)窗口。
(6)、在Reads列上分組跟蹤輸出。
(7)、使用分組的跟蹤。
2. 屢次執行開銷較大的查詢:
l 這種狀況下,Profiler中跟蹤輸出的如下列上分組:EventClass、TextData和Reads。
l 導出Profiler跟蹤表。使用內建函數fn_trace_gettable導入到一個跟蹤表。
l 訪問sys.dm_exec_query_statsDMV從生產服務器檢索信息。
把數據裝入到數據庫的一個表中
SELECT * INTO Trace_Table FROM :: FN_TRACE_GETTABLE('C:\PerformanceTrace.trc', DEFAULT)
執行下面語句查詢屢次執行的讀操做總數:
SELECT COUNT(*) AS TotalExecutions , EventClass , TextData , SUM(Duration) AS Duration_Total , SUM(CPU) AS CPU_Total , SUM(Reads) AS Reads_Total , SUM(Writes) AS Writes_Total FROM Trace_Table GROUP BY EventClass , TextData ORDER BY Reads_Total DESC
SQL Server 2008不支持在NTEXT數據類型進行分組。而TextData是ntext類型,要轉換成Nvarchar(max)
SELECT ss.sum_execution_count , t.text , ss.sum_total_elapsed_time , ss.sum_total_worker_time , ss.sum_total_logical_reads , ss.sum_total_logical_writes FROM ( SELECT s.plan_handle , SUM(s.execution_count) sum_execution_count , SUM(s.total_elapsed_time) sum_total_elapsed_time , SUM(s.total_worker_time) sum_total_worker_time , SUM(s.total_logical_reads) sum_total_logical_reads , SUM(s.total_logical_writes) sum_total_logical_writes FROM sys.dm_exec_query_stats s GROUP BY s.plan_handle ) AS ss CROSS APPLY sys.dm_exec_sql_text(ss.plan_handle) t ORDER BY sum_total_logical_readsDESC
3. 識別運行緩慢的查詢:
須要按期監視輸入的SQL查詢的執行時間,並找出運行緩慢的查詢的響應時間。可是不是全部運行緩慢的查詢都是因爲資源問題造成。如阻塞那些都有可能致使緩慢的查詢。
能夠在Duration上跟蹤。
7、執行計劃
一、 分析查詢計劃
執行計劃從右到左,從上到下的順序閱讀。每一個步驟表明得到查詢最終輸出所執行的操做。執行計劃有如下特徵:
l 若是查詢由多個查詢的批組成,每一個查詢的執行計劃按照執行的順序顯示。批中的每一個執行將有一個相對的估算開銷,整個批的總開銷爲100%。
l 執行計劃中的每一個圖標表明一個操做符。有相對的估算開銷,全部節點的總開銷爲100%。
l 執行計劃中的一個起始操做符一般表示一個數據庫對象(表或索引)的數據檢索機制。
l 數據檢索一般是一個表操做或索引操做。
l 索引上的數據檢索將是索引掃描或索引查找。
l 索引上的數據檢索的命名慣例是[表名].[索引名]。
l 數據從右到左在兩個操做之間流動,由一個鏈接箭頭表示。
l 操做符之間鏈接箭頭的寬度是傳輸行數的圖形表示。
l 同一列的兩個操做符之間的鏈接機制將是嵌套的循環鏈接,hash匹配鏈接或者合併鏈接。
l 將光標放置在執行計劃的一個節點上,顯示一個具備一些細節的彈出窗口。
l 在Properties(屬性)窗口中有完整的一組關於操做符的細節。能夠右鍵單擊操做符並選擇Properties。
l 操做符細節在頂部顯示物理和邏輯操做的類型。物理操做表明存儲引擎實際使用的,而邏輯操做是優化器用於創建估算執行計劃的結構。若是相同,只顯示物理操做。還會顯示其餘信息:I/O、CPU等。
l 操做符細節彈出窗口的Argument(參數)部分在分析中特別有用,由於顯示了優化器鎖使用的過濾或鏈接條件。
二、 識別執行計劃中開銷較大的步驟:
l 執行計劃中每一個節點顯示整個計劃中的相對開銷,整個計劃總開銷爲100%。關注最高相對開銷的節點。
l 執行計劃可能來自於一批語句,所以可能也須要查找開銷最大的語句。
l 查看節點之間鏈接箭頭的寬度。很是寬的鏈接箭頭表示對應節點之間的傳輸大量的行。分析箭頭左邊的節點以理解須要這麼多行的緣由,還要檢查箭頭的屬性。可能看到估計的行和實際的行不同,這可能由過期的統計形成。
l 尋找hash鏈接操做。對於小的數據集,嵌套的循環鏈接一般是首選的鏈接技術。
l 尋找書籤查找操做。對於大結果集的書籤操做可能形成大量的邏輯讀。
l 若是操做符上有一個歎號的警告,是須要馬上注意的領域。這些警告多是由各類問題形成的,包括沒有鏈接條件的鏈接或者丟失統計的索引和表。
l 需找執行排序操做的步驟,這表示數據沒有以正確的排序進行檢索。
三、 分析索引有效性:
要關注【掃描】,掃描表明訪問大量的行。能夠經過如下方式判斷索引有效性:
l 數據檢索操做
l 鏈接操做
有時候執行計劃中沒有【斷言】(predicate),缺少斷言意味着整個表(聚簇索引就是該表)被做爲合併鏈接操做符的輸入進行掃描。
四、 分析鏈接有效性:
SQLServer使用3中鏈接類型:
l Hash鏈接;
l 合併鏈接
l 嵌套循環鏈接
一、 Hash鏈接:
1.一、 Hash鏈接高效處理大的、未排序的、沒有索引的輸入。
1.二、 Hash鏈接使用兩個鏈接輸入:創建輸入(build input)和探查輸入(probe input)。創建輸入是執行計劃中上面的那個輸入,探查輸入是下面那個輸入。
1.三、 最多見的hash鏈接方式——in-memory hash join,整個創建輸入被掃描或計算而後在內存中創建一個hash表。每一個行根據計算的hash鍵值(相等斷言中的一組列)被插入一個hash表元中。
內存hash鏈接的示意圖:
二、 合併鏈接:
2.一、合併鏈接要求兩個輸入在合併列上排序,這將在鏈接條件中定義。若是兩個鏈接有索引,那麼鏈接輸入由該索引排序。因爲每一個鏈接輸入都被排序了,合併排序從每一個輸入獲得一行並比較是否相等。若是相等,匹配行被生成。過程被重複到全部行都被處理。
2.二、若是優化器發現鏈接輸入都在其鏈接列上排序,合併鏈接就比hash鏈接更快而被選中。
三、 嵌套循環鏈接:
3.一、始終從單獨的表中訪問有限數量的行,爲了理解使用較小結果集的效果,在查詢中下降鏈接輸入。
3.二、使用一個鏈接輸入做爲外部(outer)輸入表。另外一個做爲內部(inner)輸入表。外部表是執行計劃的上方輸入,內部表是下方輸入。外部循環逐行消費外部輸入表。內部循環爲每一個外部行執行一次,搜索內部輸入表的匹配行。
3.三、若是外部輸入至關小,內部輸入大但有索引,嵌套循環鏈接是很是高效的。鏈接經過犧牲其餘方面來提升速度——使用內存來取得小的數據集並快速與第二個數據集比較。合併排序與此相似,使用內存和一小部分tempdb排序,hash鏈接使用內存和tempdb創建hash表。
3.四、雖然循環鏈接更快,可是隨着數據集變得更大,比hash或合併消耗更多的內存。因此SQL Server會在不一樣數據集的狀況下使用不一樣計劃的緣由。
3種鏈接類型的特性:
鏈接類型 |
鏈接列上的索引 |
鏈接表的通常大小 |
預先排序 |
鏈接子句 |
Hash |
內部表:不須要索引 外部表:可選 最佳條件:小的外部表,大的內部表 |
任意 |
不須要 |
Equi-join |
合併 |
內部/外部表:必須 最佳條件:兩個表都有聚簇索引或覆蓋索引 |
大 |
須要 |
Equi-join |
嵌套循環 |
內部表:必須 外部表:最好有 |
小 |
可選 |
全部 |
注意:在hash和嵌套循環鏈接中,外部表通常是兩個鏈接表中較小的一個。
五、 實際執行計劃vs估算執行計劃:
估算執行計劃對臨時表沒法生成。
六、 計劃緩存:
通常是保存在內存空間。可使用DMV來查詢:
SELECT p.query_plan , t.text FROM sys.dm_exec_cached_plansr CROSS APPLY sys.dm_exec_query_plan(r.plan_handle) p CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) t
8、查詢開銷
一、 客戶統計:將計算機做爲服務器的一個客戶端,從這個角度去發出捕捉執行信息。
點擊SSMS中的【查詢】→【包含客戶統計】,但這一步不是很好的收集方法。有時候須要重置:【查詢】→【重置客戶統計】
二、 執行時間:
Duration和CPU都表明着查詢的時間因素,可使用SET STATISTICS TIME來取得執行時間。
其中最後一行的CPU時間等於Profiler的CPU值,佔用時間表明Duration值。0毫秒的分析和編譯時間說明重用了執行計劃。能夠執行:DBCC FREEPROCCACHE清除緩存。可是不要在生產系統上執行,由於某種狀況下,這和重啓的開銷相同。
三、 STATISTICS IO:
Profiler獲取的Reads列的讀取次數嚐嚐是Duration、CPU、Reads和Writes這些因素中最重要的。在解讀STATISTICS IO的輸出時,多半參考【邏輯讀】操做。有時候也會參考掃描計數。物理讀操做和預讀數量在數據不能在內存中找到時將不爲0,但一旦數據填寫到內存,物理讀和預讀將趨向於0。在優化期間,能夠監控單表的讀操做次數以確保確實減小了該表的數據訪問開銷。