When doing a performance investigation a useful thing to do is look at what data is present in the buffer pool. This can be used to analyze impact of running a query on the state of data pages in buffer pool. By collecting the pre and post picture of buffer pool, you can see the cost of running a query in terms of physical IOs that happened. You may argue that this can be done by looking at statistics IO output; however if you are running a series of queries and want to see a consolidated data and not data about individual queries, this query is a great help.
The contents of the buffer pool can also reveal which pages are accessed most frequently by your applications and often reflect the actual I/O that is happening. How can frequently access pages in memory also cause disk I/O? When lots of different objects are accessed overtime the proportion of data in the buffer pool reflects the frequency of access. This happens because data pages of infrequently accessed objects get kicked out of main memory over time.
If you are not familiar with the buffer pool, it contains several types of objects such as data pages and plans. For more information on the buffer pool see Buffer Management http://msdn2.microsoft.com/en-us/library/aa337525.aspx
The following query can be used to look at the contents of the buffer pool -
select
count(*)as cached_pages_count,
obj.name as objectname,
ind.name as indexname,
obj.index_id as indexid
from sys.dm_os_buffer_descriptors as bd
inner join
(
select object_id as objectid,
object_name(object_id) as name,
index_id,allocation_unit_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_id as objectid,
object_name(object_id) as name,
index_id,allocation_unit_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 outer join sys.indexes ind
on obj.objectid = ind.object_id
and obj.index_id = ind.index_id
where bd.database_id = db_id()
and bd.page_type in ('data_page', 'index_page')
group by obj.name, ind.name, obj.index_id
order by cached_pages_count desc
An example of what it returns –
1. Run following command to remove all clean data pages from the buffer pool – (DO NOT TRY THIS COMMAND ON PRODUCTION MACHINES)
DBCC DROPCLEANBUFFERS
Running buffer pool analysis query had following results –
cached_pages_count ObjectName IndexName IndexId
------------------ ------------------ ---------------------- -----------
15 sysobjvalues clst 1
3 sysallocunits clust 1
2 syshobtcolumns clust 1
2 sysrowsetcolumns clust 1
2 sysrowsets clust 1
2 sysschobjs clst 1
…
2. Run the following query on AdventureWorks database –
select * from Person.Address
where city like 'Bothell'
This is going to read from disk the data pages needed to execute the query. Run the buffer pool analysis query again to see the change.
cached_pages_count ObjectName IndexName IndexId
------------------ ------------------ ---------------------- -----------
278 Address PK_Address_AddressID 1
15 sysobjvalues clst 1
4 sysmultiobjrefs clst 1
3 sysallocunits clust 1
2 syshobtcolumns clust 1
2 sysrowsetcolumns clust 1
…
As you can see now there are data pages in buffer pool from the Address table. Additionally since only clustered index pages for Address table are present, no other indexes were used in the query.
Another tool which can help in this case is DBCC MEMORYSTATUS output. The advantage of the query in the entry is nice result set which can be stored in a temp table.
Authors:
Gaurav Bindlish