這些天看了一篇微軟官方發布的MS SQL Server2008性能問題處理及優化的英文文檔,里面知識點介紹地很詳細,在現實工作中也很實用,遂產生了想把它翻譯一下的念頭。翻譯的過程,既可以幫助自己復習一下這些技術,也可以向其他還不熟悉這一塊的朋友介紹一些新的知識,何樂而不為呢。只是這篇文章有點長,我會分成幾篇隨筆去介紹,所以,不光是對我耐性的考驗,也是對你的考驗哦!
-------------------------------------------
識別長的阻塞
正如之前提到的,阻塞在SQL Server中是很正常的並且只是邏輯鎖為了維護事務一致性的外在表現。然而,當等待超出了閾值,它就會影響相應的速度。為了識別那些長時間運行的阻塞,你可以使用BlockedProcessThreshold配置參數去創建一個用戶--配置的服務器端的阻塞閾值。這個閾值定義的單位是秒。任何超出這個閾值的阻塞都會觸發一個會被SQL Trace追蹤到的trace event。
例如,一個200秒的阻塞運行閾值可以使用下面的步驟在SSMS中配置:
1. 運行語句:sp_configure 'blocked process threshold',200
2. 過去的配置就會被重載
3. 阻塞運行閾值一旦被建立后,追蹤trace event. 這樣就會被SQL Trace或者SQL Server Profiler追蹤到了。
4. 如果你正在使用SQL Trace, 使用sp_trace_setevent和event_id=137
5. 如果你正在使用SQL Server Profiler,選擇Blocked process report event類別(在Errors和Warnings對象下面)。請看截圖1.
截圖1:捕獲長阻塞和死鎖
注意 這是一個輕量級的捕獲,因為僅僅追蹤了一個阻塞查過了閾值,或者一個死鎖發生的情況。對於每一個200秒鍾的間隙,當某個鎖被阻塞了,一個捕獲事務就會被激發。這也就意味着說一個單獨的鎖如果超過了600秒鍾,那它就會導致生成三個捕獲事件。看截圖2.
截圖2:Reporting Blocking>阻塞閾值
捕獲事件包含整個的SQL 語句,既包括引起阻塞的語句,也包含被阻塞的語句。在當前這個例子里,UPDATE Customers語句阻塞了SELECT From Customers語句。
使用sys.dm_db_index_operational_stats查看每一對象的阻塞情況
DMV sys.dm_db_index_operational_stats提供了完整的索引使用情況統計,包括阻塞。從阻塞層面來看,它提供了詳細的在每個表,每個分區上的鎖數量的統計信息。比如說包含對於某個給定表或者索引的歷史信息的訪問,locks(row_lock_count),blocks(process_virtual_memory_low),以及waits(row_lock_wait_in_ms)。
從這個DMV中可以獲取到的有用信息類型包括:
- 持有的鎖的數量,例如,行或者頁.
- 阻塞或者等待的數量,例如,行或者頁.
- 阻塞或者等待的時間,例如,行或者頁.
- 頁閂等待數量的等待。page_latch_wait的等待時間:這個包含在某個特定頁上競爭,比如說,遞增的主鍵插入。在這種情況下,熱點是最后一頁,所以多個寫入者往相同的最后一頁中同時嘗試加上頁閂.這個會被顯示為Pagelatch waits。
- page_io_latch_wait等待時間:當用戶需要的某個頁不再緩沖池中時,一個I/O閂就會發生.一個緩慢的或者超負荷的I/O子系統可能有些時候會遇到PageIOlatch等待,實際上I/O問題。這些問題會因緩存溢出或者缺失索引而引起。
- 頁閂等待時間。
其它類型的信息也用來訪問索引:
- 訪問類型,例如,范圍或者單一查找。
- 在葉級別的insert,update,以及delete。
- 比葉層級高的層級上的Insert,update以及delete。在葉層級之上的活動是索引維護。每個葉的第一行有一個上面層級的入口。如果一個新的頁被安排在了葉上,上面的層級將會在葉的第一行上有一個新的入口。
- 在葉層級的頁合並。這些被釋放頁之前的顯示結果是空的,因為它們里面的行都已經被刪除掉了。
- 索引維護。在比葉層級高的層級上合並的頁都是空頁,這些頁會被釋放掉,因為沒有行在葉上被刪除,這就導致了保留中間層的頁是空的。每個葉上的第一行都有一個跟上面層級的接口。如果足夠的行在葉層級被刪除,中間層級的那些包含原始入口的第一行的索引頁就會是空的。
這些信息是在實例啟動以后就逐漸積累的。這些信息在實例被重啟以后就不復存在了,並且沒有任何位置去重新設定它。這個DMV返回過來的信息只有在元數據緩存對象對象中顯示堆或者索引可用時才會存在。無論什么何時,當堆或者索引的元數據被帶進去元數據緩存時,每列的數據會被設定為0。統計信息會被積累,直到緩存對象從元數據緩存中被移除。然而,你可以間隙性地到處這些數據到其它的表中,一直去你可以更深層次地進行查詢。
使用等待縱覽阻塞造成的性能影響
SQL Server 2008提供了超過100種等待類型去捕獲應用程序性能。任何時間出現了用戶連接的等待,SQL Server開始累積等待時間。比如,應用程序請求的資源,例如I/O,鎖,或者內存並且可以等待資源變得可用。這種針對所有連接的等待信息被概要化並被類別化,以至於可以使用一個性能profile去獲取已經給定的工作負載。這樣的話,SQL等待類別識別並且分類來自於應用程序負載或者用戶透視圖的用戶等待。
這個查詢列舉出了SQL Server中的10個top等待。這些等待是被累積計算的但是你可以使用DBCC SQLPERF([sys.dm_os_wait_stats],clear)去重新設定它們。
select top 10 * from sys.dm_os_wait_stats order by wait_time_ms desc
下面是查詢輸出結果。一些需要注意的關鍵點有:
- 一些等待是正常的,例如有些由后台進程導致的,例如lazy writer引發的等待。
- 一些會話為了獲取一個SH鎖等待了很長時間。
- 一個顯著的等待:一個工作者已經被授權了訪問資源及它被指定使用CPU的時間之間的間隔。一個較長的顯著等待往往預示着存在CPU競爭。
wait_type waiting_tasks_count wait_time_ms max_wait_time_ms signal_wait_time_ms
------------------ -------------------- -------------------- -----------------------------
LAZYWRITER_SLEEP 415088 415048437 1812 156
SQLTRACE_BUFFER_FLUSH 103762 415044000 4000 0
LCK_M_S 6 25016812 23240921 0
WRITELOG 7413 86843 187 406
LOGMGR_RESERVE_APPEND 82 82000 1000 0
SLEEP_BPOOL_FLUSH 4948 28687 31 15
LCK_M_X 1 20000 20000 0
PAGEIOLATCH_SH 871 11718 140 15
PAGEIOLATCH_UP 755 9484 187 0
IO_COMPLETION 636 7031 203 0
為了分析等待狀況,你需要間歇性地查詢這些數據然后分析它。
會話-級別的等待統計
為了識別會話-級別或者語句-級別的等待統計,在過去的SQL Server版本中不太可能,Extend Event工具只是一個想象中的工作。現在可以在整個運行階段伸縮性捕獲等待統計信息。
下面這個實例Extended Events 會話捕獲所有的等待,包括SQL Server內部及外部的,比如id為54的會話。
-- sqlserver.session_id is the ID of the target session you want to trace. I am using 54 in the example. Replace it accordingly -- make sure C:\xevent folder exists create event session session_waits on server add event sqlos.wait_info (action (sqlserver.sql_text, sqlserver.plan_handle, sqlserver.tsql_stack) WHERE sqlserver.session_id=54 and duration>0) , add event sqlos.wait_info_external (action (sqlserver.sql_text, sqlserver.plan_handle, sqlserver.tsql_stack) WHERE sqlserver.session_id=54 and duration>0) add target package0.asynchronous_file_target (SET filename=N'C:\xevent\wait_stats.xel', metadatafile=N'C:\xevent\wait_stats.xem'); alter event session session_waits on server state = start; go -- wait for monitored workload in target session (54 in this example) to finish.
為了從輸出文檔中讀取結果值,運行下面的查詢:
alter event session session_waits on server state = stop drop event session session_waits on server select CONVERT(xml, event_data).value('(/event/data/text)[1]','nvarchar(50)') as 'wait_type', CONVERT(xml, event_data).value('(/event/data/value)[3]','int') as 'duration', CONVERT(xml, event_data).value('(/event/data/value)[6]','int') as 'signal_duration' into #eventdata from sys.fn_xe_file_target_read_file (N'C:\xevent\wait_stats*.xel', N'C:\xevent\wait_stats*.xem', null, null) -- save to temp table, #eventdata select wait_type, SUM(duration) as 'total_duration', SUM(signal_duration) as 'total_signal_duration' from #eventdata group by wait_type drop table #eventdata go
實例輸出如下:
wait_type total_duration total_signal_duration
-------------------------------------------------- -------------- ---------------------
NETWORK_IO 233 0
PREEMPTIVE_OS_WAITFORSINGLEOBJECT 231 576
WAITFOR 7000 0
PAGEIOLATCH_UP 624 0
PAGELATCH_UP 2320 45
PAGELATCH_SH 45 10
WRITELOG 30 0