我在做SQL Server 7.0技術支持的時候有客戶問我,“我的SQL Server buffer pool很大,有辦法知道是哪些對象吃掉我的buffer Pool內存麼?比方說,能否知道是哪個數據庫,哪個表,哪個index佔用了buffer Pool麼?”當時我沒有找到這個問題的答案,但是我一直記着這個問題。直到SQL server 2005 版本出現,這個問題迎刃而解。答案就是使用動態視圖(DMV) sys.dm_os_buffer_descriptors。
這個DMV非常強大。根據SQL Server 聯機叢書,這個視圖的作用是 “返回有關 SQL Server 緩衝池中當前所有數據頁的信息。可以使用該視圖的輸出,根據數據庫、對象或類型來確定緩衝池內數據庫頁的分佈”。具體點說,這個視圖能夠返回buffer pool裏面一個8K 的data page的下列屬性:
(1)該頁屬於哪個數據庫
(2)該頁屬於數據庫哪個文件
(3)該頁的Page_ID
(4)該頁的類型。可以根據這個來判斷此頁時索引頁還是數據頁
(5)該頁內有多少行數據
(6)該頁有多少可用空間。
(7)該頁從磁盤讀取以來是否修改過。
有了上面的信息,我們就可以很方便的統計出幾種很有用的數據,如下。
1. Buffer Pool的內存主要是由那個數據庫佔了?
SELECT count(*)*8 as cached_pages_kb,CASE database_id
WHEN 32767 THEN 'ResourceDb'
ELSE db_name(database_id)
END AS Database_name
FROM sys.dm_os_buffer_descriptors
GROUP BY db_name(database_id) ,database_id
ORDER BY cached_pages_kb DESC;
結果如下:
從上面的結果可以看到數據庫AdventureWorks佔用了大概30MB左右的緩衝池空間。
注意該DMV 並不返回Buffer Pool裏面有關非數據頁(如執行計劃的緩存等)的信息。也就是說這個DMV並沒有返回Buffer Pool裏面所有頁面的信息。
2. 再具體一點,當前數據庫的哪個表或者索引佔用Pool緩衝空間最多?
SELECT count(*)*8 AS cached_pages_kb
,obj.name ,obj.index_id,b.type_desc,b.name
FROM sys.dm_os_buffer_descriptors AS bd
INNER JOIN
(
SELECT object_name(object_id) AS name
,index_id ,allocation_unit_id,object_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.hobt_id
AND (au.type = 1 OR au.type = 3)
UNION ALL
SELECT object_name(object_id) AS name
,index_id, allocation_unit_id,object_id
FROM sys.allocation_units AS au
INNER JOIN sys.partitions AS p
ON au.container_id = p.partition_id
AND au.type = 2
) AS obj
ON bd.allocation_unit_id = obj.allocation_unit_id
LEFT JOIN sys.indexes b on b.object_id = obj.object_id AND b.index_id = obj.index_id
WHERE database_id = db_id()
GROUP BY obj.name, obj.index_id ,b.name,b.type_desc
ORDER BY cached_pages_kb DESC;
輸出結果如下 (部分):
從上面的結果可以看到表Individual 在Pool內存裏面緩衝最多,可能這個就是經常訪問的熱表,或者是比較大的表。注意Pool裏面的緩衝頁是經常變化的。 你如果再跑一次語句,出現在頭條的可能是另外一個表了。
3. Buffer Pool緩衝池裏面修改過的頁總數大小。這個比較容易:
SELECT count(*)*8 as cached_pages_kb,
convert(varchar(5),convert(decimal(5,2),(100-1.0*(select count(*) from sys.dm_os_buffer_descriptors b where b.database_id=a.database_id and is_modified=0)/count(*)*100.0)))+'%' modified_percentage
,CASE database_id
WHEN 32767 THEN 'ResourceDb'
ELSE db_name(database_id)
END AS Database_name
FROM sys.dm_os_buffer_descriptors a
GROUP BY db_name(database_id) ,database_id
ORDER BY cached_pages_kb DESC;
結果:
從上面的結果可以看到,AdventureWorks數據庫大概有13.84%的數據是修改過的。如果一個數據庫的大部分(超過80%) 是修改過的,那麼這個數據庫寫操作非常多。反之如果這個比例接近0,那麼該數據庫的活動幾乎是只讀的。讀寫的比例對磁盤的安排是很重要的。當然還有其他性能數據來獲得數據庫讀寫的大概比例,這裏限於篇幅就不多談了。