一、SQL SERVER DMO 簡介
簡介:
從SQLServer2005開始,微軟引入了一個名叫DMO(動態管理對象)的新特性,DMO可以分爲DMFs(Dynamic Manage Functions,動態管理函數)和DMVs(Dynamic Manage Views,動態管理視圖)兩部分。這些函數和視圖用於查找SQLServer實例內部統計信息以供性能監控所用。它們提供實時的,關於SQLServer內部工作的,能用於性能分析和性能故障排除的各種統計信息。
所有的DMO都屬於sys架構,並且以dm_開頭。執行DMO需要有VIEW SERVER STATE和VIEW DATABASE STATE權限。
下面簡述一下本系列將要介紹的DMO:
Ø 執行相關的DMO(sys.dm_exec_*):提供與執行相關的統計信息。可以用於監控與緩存查詢、執行計劃、活動連接/會話和帶有執行計劃的當前運行的查詢的相關統計信息。
Ø 索引相關的DMO(sys.dm_db_index_*和sys.dm_db_missing_*):提供關於索引的統計信息。這些DMO可以用於監控和分析因爲丟失索引、無效索引而導致的性能問題,也可以用來檢查索引的使用情況。
Ø 數據庫相關DMO(sys.dm_db_*):提供數據庫相關統計信息。可以用於監控和分析數據庫的性能問題,分析數據庫相關文件的統計信息、會話統計信息和任務統計信息。
Ø I/O相關DMO(sys.dm_io_*):提供I/O操作的統計信息,用於監控和分析SQLServer的I/O性能問題。
Ø OS相關DMO(sys.dm_os_*):提供關於sqlos內部統計信息,用於監控和分析服務器配置問題。
Ø 事務相關的DMO(sys.dm_trn_*):提供事務相關的統計信息,用於監控和分析長時間運行的事務的鎖定、死鎖問題。
這些DMO的數據可以通過DBCC SQLPERF(‘SYS.DM_OS_WAIT_STATS’,CLEAR)或者重啓服務器來重置。
監控當前查詢:
SELECT DB_NAME(R.database_id) AS DatabaseName ,
S.original_login_name AS LoginName ,
S.host_name AS ClientMachine ,
S.program_name AS ApplicationName ,
R.start_time AS RequestStartTime ,
ST.text AS SQLQuery ,
QP.query_plan AS ExecutionPlan ,
R.cpu_time AS CPUTime ,
R.total_elapsed_time AS TotalTimeElapsed ,
R.open_transaction_count AS TotalTransactionOpened ,
R.reads ,
R.logical_reads ,
R.writes AS TotalWrites
FROM sys.dm_exec_requests AS R
INNER JOIN sys.dm_exec_sessions AS S ON R.session_id = S.session_id
CROSS APPLY sys.dm_exec_sql_text(R.sql_handle) AS ST
CROSS APPLY sys.dm_exec_query_plan(R.plan_handle) AS QP
ORDER BY TotalTimeElapsed DESC
GO
監控當前打開的遊標:
SELECT S.host_name AS ClientMachine ,
S.program_name AS ApplicationName ,
S.original_login_name AS LoginName ,
C.name AS CursorName ,
C.properties AS CursorOptions ,
C.creation_time AS CursorCreatinTime ,
ST.text AS SQLQuery ,
C.is_open AS IsCursorOpen ,
C.worker_time / 1000 AS DurationInMiliSeconds ,
C.reads AS NumberOfReads ,
C.writes AS NumberOfWrites
FROM sys.dm_exec_cursors (0) AS C
INNER JOIN sys.dm_exec_sessions AS S ON C.session_id = S.session_id
CROSS APPLY sys.dm_exec_sql_text(C.sql_handle) AS ST
ORDER BY DurationInMiliSeconds DESC
GO
分析:
在上面步驟中,使用了以下的DMOs:
Ø Sys.dm_exec_requests
Ø Sys.dm_exec_sessions
Ø Sys.dm_exec_sql_text
Ø Sys.dm_exec_query_plan
對於上面的查詢結果,需要思考的問題:
Ø 哪個庫正在接受請求?
Ø 那個登錄名執行了這個請求?
Ø 請求是從哪個計算機發出的?
Ø 請求是從那個應用程序發出的?
Ø 請求是何時到達SQLServer的?
Ø 請求中需要執行什麼SQL語句?
Ø 執行的SQL語句的執行計劃是什麼?
Ø 請求的持續時間有多少?
Ø 請求是否開啓了事務?
Ø 請求造成的讀寫數是多少?
Ø 請求是否被阻塞了?如果是,是哪個會話造成的?
爲了找到這些信息,需要把sys.dm_exec_requests和sys.dm_exec_sessions的session_id列關聯。
同時,使用CROSS APPLY來關聯sys.dm_exec_sql_text()函數來查找請求的SQL文本。關聯sys.dm_exec_query_plan()函數來查找請求的執行計劃。這兩個函數需要從查詢中分別獲得sql_handle和plan_handle。在結果集中,按TotalTimeElapsed列排序,可以知道最耗資源的查詢。
第二個查詢中使用了sys.dm_exec_cursors()函數來返回當前正在使用的遊標的詳細。這個函數接受session_id作爲參數。如果傳入了特定session_id,只會返回該會話的遊標,如果傳入0,則返回所有會話的遊標。結果集按照DurationInMiliSecondes排序,一邊查找最耗資源的遊標,注意worker_time除以了1000,因爲這個的單位是微妙,除以1000可以得到毫秒。
二、用DMV和DMF監控索引性能
SQLServer有專門的DMO來顯示索引相關統計信息。能幫助你分析現有索引的性能情況。通過這些DMO,可以做到:
Ø 檢查索引使用模式
Ø 查找丟失索引
Ø 查找無用索引
Ø 查找索引碎片
Ø 分析索引頁分配明細
下面將使用這些DMO來檢查數據庫的丟失索引,索引上的查找和掃描操作,並分析索引碎片是否有必要重組或重建。
分析:
SQLServer提供缺失索引的詳細情況。缺失索引是指在數據庫中不存在的索引,但是如果創建,查詢將會得益並運行得更快。可以關聯兩個DMV,sys.dm_db_missing_index_groups和sys.dm_db_missing_index_group_stats。其中DMV,sys.dm_db_missing_index_groups_stats返回關於如果創建了缺失索引,將會對查詢有多少可能性的提高的詳細信息。注意從這個視圖中查找的avg_user_impact列,這裏間接地通過這列來和Sys.dm_db_missing_index_details與sys.dm_db_missing_index_groups關聯。
和DTA(數據庫優化引擎顧問)類似,DMV可能會建議廣泛使用很多INCLUDE列在索引中。所以你不應該不顧一切把所有索引全部創建。因爲這對你的DML操作可能會帶來比較大的影響,如增刪改等操作。
在第二個查詢DMO中,返回了特定索引的查找和掃描數量。爲了返回索引名稱和對象名稱,關聯了sys.indexes和sys.objects這兩個目錄視圖。
最後就是查找碎片,這裏使用了DMF而不是DMV,sys.dm_db_index_physical_stats()函數。留意一下,這裏使用了DB_ID()函數,因爲希望得到當前數據庫的所有索引信息,所以用這個函數即可。爲了得到索引名和對象名,關聯了sys.indexex和sys.objects目錄視圖。
三、用DMV和DMF監控TempDB
前言:
我們都知道TempDB是SQLServer的系統數據庫,且SQLServer的日常運作嚴重依賴這個庫。因此,監控TempDB的性能問題尤爲重要。在過去很長一段時間裏面,很多人都忽略了TempDB的重要性並忽略了它的性能問題。這並不是一件好事,因爲TempDB的性能會影響其他用戶數據庫的性能,所以需要時時刻刻注意TempDB的性能。
在一些查詢的聚合、排序操作,遊標操作和版本存儲操作,聯機索引創建,用戶對象存儲如臨時表等,都將用到TempDB,作爲DBA,需要經常監控TempDB,以便識別出資源消耗較大的操作。此時可以使用數據庫相關的DMVs來完成。
在使用這些DMVs時,要清楚一些基礎概念,SQLServer是如何組織數據的。所以先來了解頁和區。
就像你所知道的,SQLServer主要通過兩類文件來存儲數據庫。就是數據文件(mdf/ndf)和日誌文件(ldf)。這裏只討論數據文件。因爲頁和區不適用於日誌文件。
數據文件是SQLServer存儲數據庫的對象如表和索引的一種格式化文件。這些數據文件由更小的單元組成,這些單元叫做頁。一個頁存放8K的數據。
另外,區也有頁來存放,一個區有8個順序頁組成。所以,一個區有64K,1MB有16個區。
包含數據的對象會分配到區中的頁上。有兩類的區——統一區和混合區,一個統一區被一個單獨對象所獨有,混合區可以存放能夠放進8個頁的8個不同對象。因爲混合區可以共享整個區,所以也叫做共享區。當表很小時,會放入混合區,直到足夠大佔據一個區時,混合區就會整合成一個統一區。
本文將演示如何監控TempDB的性能。同時可以識別出引起TempDB空間增加的會話和任務。
準備工作:
本文將產生1000萬數據,並存放到TempDB的局部臨時表中。然後監控頁分配和重新分配的情況。
步驟:
1、 連到SQLServer
2、 輸入以下代碼:
USE tempdb
GO
--檢查表是否存在
IF OBJECT_ID('[dbo].[tbl_TempDBStats]') IS NOT NULL
DROP TABLE [dbo].[tbl_TempDBStats]
--創建表用於存放頁分配的明細
CREATE TABLE [dbo].[tbl_TempDBStats]
(
session_id SMALLINT ,
database_id SMALLINT ,
user_objects_alloc_page_count BIGINT ,
user_objects_dealloc_page_count BIGINT ,
internal_objects_alloc_page_count BIGINT ,
internal_objects_dealloc_page_count BIGINT
)
GO
--收集當前會話在執行查詢之前的分配明細
INSERT INTO [dbo].[tbl_TempDBStats]
SELECT session_id ,
database_id ,
user_objects_alloc_page_count ,
user_objects_dealloc_page_count ,
internal_objects_alloc_page_count ,
internal_objects_dealloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id = @@SPID
GO
--檢查表是否存在
IF OBJECT_ID('TempDB.dbo.#tbl_SampleData') IS NOT NULL
DROP TABLE TempDB.dbo.#tbl_SampleData
GO
--產生萬數據並插入臨時表
SELECT TOP 10000000
SC1.object_id ,
SC1.column_id ,
SC1.name ,
SC1.system_type_id
INTO TempDB.dbo.#tbl_SampleData
FROM sys.columns AS SC1
CROSS JOIN sys.columns AS SC2
CROSS JOIN sys.columns AS SC3
ORDER BY SC1.column_id
GO
--重新收集插入數據後的數據頁分配情況
INSERT INTO [dbo].[tbl_TempDBStats]
SELECT session_id ,
database_id ,
user_objects_alloc_page_count ,
user_objects_dealloc_page_count ,
internal_objects_alloc_page_count ,
internal_objects_dealloc_page_count
FROM sys.dm_db_session_space_usage
WHERE session_id = @@SPID
3、 然後輸入以下代碼,並注意執行前後的數據差異:
USE tempdb
GO
SELECT *
FROM [dbo].[tbl_TempDBStats]
4、 結果如下:
5、 運行以下查詢查找TempDB空間分配情況:
SELECT DB_NAME(FSU.database_id) AS DatabaseName ,
MF.name AS LogicalFileName ,
MF.physical_name AS PhysicalFilePath ,
SUM(FSU.unallocated_extent_page_count) * 8.0 / 1024 AS Free_Space_In_MB ,
SUM(FSU.version_store_reserved_page_count
+ FSU.user_object_reserved_page_count
+ FSU.internal_object_reserved_page_count
+ FSU.mixed_extent_page_count) * 8.0 / 1024 AS Used_Space_In_MB
FROM sys.dm_db_file_space_usage AS FSU
INNER JOIN sys.master_files AS MF ON FSU.database_id = MF.database_id
AND FSU.file_id = MF.file_id
GROUP BY FSU.database_id ,
FSU.file_id ,
MF.name ,
MF.physical_name
6、 結果如下:
分析:
在本文的開片中,首先創建了一個表tbl_TempDBStats以便存放頁分配和釋放的統計數據。然後通過查詢sys.dm_db_session_space_usage,獲取分析信息。把所有用戶定義對象和系統內置對象都插入表中。
下一個查詢將產生1000萬數據,並插入臨時表#tbl_SampleData。使tempdb的分配情況發生改變。
插入數據以後,檢查tbl_TempDBStats表,可以得出一些對比信息,最後通過一個DMV,sys.dm_db_file_space_usage。可以看出以MB爲單位的分配情況。
注意:sys.dm_db_file_space_usage ,sys.dm_db_session_space_usage這兩個DVM僅適用於tempdb。
四、用DMV和DMF監控磁盤IO
前言:
本文爲本系列最後一篇,作爲DBA,你必須經常關注磁盤的I/O問題,一旦出現問題,要儘快分析出是什麼問題。SQLServer同樣提供了一些列與I/O相關的DMO來做監控。
本文介紹如何使用DMO來監控I/O子系統的性能並找到I/O瓶頸。通過本文,可以區分不同數據庫的I/O使用模式。一旦發現有數據庫的I/O很高,可能需要考慮把數據庫遷移到單獨的磁盤,或者深入研究I/O產生的問題。
1. 監控SQLServer實例上的日誌文件和數據文件:
SELECT DB_NAME(VFS.database_id) AS DatabaseName ,
MF.name AS LogicalFileName ,
MF.physical_name AS PhysicalFileName ,
CASE MF.type
WHEN 0 THEN 'Data File'
WHEN 1 THEN 'Log File'
END AS FileType ,
VFS.num_of_reads AS TotalReadOperations ,
VFS.num_of_bytes_read AS TotalBytesRead ,
VFS.num_of_writes AS TotalWriteOperations ,
VFS.num_of_bytes_written AS TotalWriteOperations ,
VFS.io_stall_read_ms AS TotalWaitTimeForRead ,
VFS.io_stall_write_ms AS TotalWaitTimeForWrite ,
VFS.io_stall AS TotalWaitTimeForIO ,
VFS.size_on_disk_bytes AS FileSizeInBytes
FROM sys.dm_io_virtual_file_stats(NULL, NULL) AS VFS
INNER JOIN sys.master_files AS MF ON VFS.database_id = MF.database_id
AND VFS.file_id = MF.file_id
ORDER BY VFS.database_id DESC
2. 清空數據緩存:
DBCC DROPCLEANBUFFERS
GO
3. 再次執行監控SQLServer實例上的日誌文件和數據文件:
4. 查看是否有IO掛起操作:
SELECT DB_NAME(VFS.database_id) AS DatabaseName ,
MF.name AS LogicalFileName ,
MF.physical_name AS PhysicalFileName ,
CASE MF.type
WHEN 0 THEN 'Data File'
WHEN 1 THEN 'Log File'
END AS FileType ,
PIOR.io_type AS InputOutputOperationType ,
PIOR.io_pending AS Is_Request_Pending ,
PIOR.io_handle ,
PIOR.scheduler_address
FROM sys.dm_io_pending_io_requests AS PIOR
INNER JOIN sys.dm_io_virtual_file_stats(DB_ID('AdventureWorks'), NULL)
AS VFS ON PIOR.io_handle = VFS.file_handle
INNER JOIN sys.master_files AS MF ON VFS.database_id = MF.database_id
AND VFS.file_id = MF.file_id
GO