4.0 Useful Queries on DMV’s to understand Plan Cache Behavior
Published Mar 23 2019 04:50 AM 1,726 Views
Microsoft
First published on MSDN on Jan 23, 2007

<br/><!--<br/> p.MsoNormal<br/> {mso-style-parent:"";<br/> margin-bottom:.0001pt;<br/> font-size:12.0pt;<br/> font-family:"Times New Roman";<br/> margin-left:0in; margin-right:0in; margin-top:0in}<br/> li.MsoNormal<br/> {mso-style-parent:"";<br/> margin-bottom:.0001pt;<br/> font-size:12.0pt;<br/> font-family:"Times New Roman";<br/> margin-left:0in; margin-right:0in; margin-top:0in}<br/>--><br/>



The following queries can be used to understand plan cache behavior. It should be noted that for systems with large plan caches, selecting all rows and joining with sys.dm_exec_sql_text is not recommended. To find the size of the plan cache use:



select (sum(single_pages_kb) + sum(multi_pages_kb) ) * 8  / (1024.0 * 1024.0) as plan_cache_in_GB


from sys.dm_os_memory_cache_counters


where type = 'CACHESTORE_SQLCP' or type = 'CACHESTORE_OBJCP'


go




  1. To view the cached plans use the query sys.dm_exec_cached plans and sys.dm_exec_sql_text. The query below gives the sql text of the query, number of times the query has be executed (or reused), cacheobjtype (Compiled Plan/Extended Stored


  1. Procedure/Parse Tree), objtype (View/Proc/Adhoc), bucketid in the hash table these plans are hashed to, and the plan handle.


select top 1000 st.text, cp.cacheobjtype, cp.objtype, cp.refcounts, cp.usecounts, cp.size_in_bytes, cp.bucketid, cp.plan_handle


from sys.dm_exec_cached_plans cp


cross apply sys.dm_exec_sql_text(cp.plan_handle) st


where cp.cacheobjtype = 'Compiled Plan'


and cp.objtype = 'Prepared'


order by cp.usecounts desc


go



select top 1000 st.text, cp.cacheobjtype, cp.objtype, cp.refcounts, cp.usecounts, cp.size_in_bytes, cp.bucketid, cp.plan_handle


from sys.dm_exec_cached_plans cp


cross apply sys.dm_exec_sql_text(cp.plan_handle) st


where cp.cacheobjtype = 'Compiled Plan'


and cp.objtype = 'Adhoc'


order by cp.usecounts desc


go



Parameterization of queries gives a significant performance benefit. Parameterized queries have objtype 'Prepared'. Prepared queries typically have large usecounts and are greater in size than the corresponding adhoc shell queries (less than 50K for adhoc shell queries). Plans for stored procedures also have a high degree of reuse. In some workloads, there is reuse of adhoc queries with the exact same parameter values. In such cases caching of the shell query proves gives better throughput.



Sorting the data on usecounts gives the information regarding the degree of reuse of queries. The query below sorts the cached plans on the plan size. This query can be used to identify large plans. Caching several un-parameterized adhoc queries with large plan size and with no reuse will lead to plan cache bloating. This causes the plan cache to be under constant memory pressure and gives suboptimal performance results. It is therefore important to try to parameterize queries.



select top 1000 st.text, cp.cacheobjtype, cp.objtype, cp.refcounts,


cp.usecounts, cp.size_in_bytes, cp.bucketid, cp.plan_handle


from sys.dm_exec_cached_plans cp


cross apply sys.dm_exec_sql_text(cp.plan_handle) st


where cp.cacheobjtype = 'Compiled Plan'


and (cp.objtype = 'Adhoc' or cp.objtype = 'Prepared')


order by cp.objtype desc, cp.size_in_bytes desc


go




  1. The DMV sys.dm_os_memory_cache_entries has the number of 8KB pages allocated for the plan, the number of disk IO's associated with this entry, the number of context switches associated with this query, the original and current cost for the entry. Original cost of the entry is an approximation of the number of I/Os incurred, memory, and the context switch count. The current cost of the entry is the actual cost associated with the query. A query is inserted into the cache with a zero current cost. Its current cost is incremented by one on every re-use. The maximum value of the current cost is the original cost of query. Entries with zero current cost will be removed when the plan cache is under memory pressure. Use either query below to get this information:


Select top 1000 st.text, cp.objtype, cp.refcounts, cp.usecounts, cp.size_in_bytes,


ce.disk_ios_count, ce.context_switches_count,


ce.pages_allocated_count, ce.original_cost, ce.current_cost


from sys.dm_exec_cached_plans cp


cross apply sys.dm_exec_sql_text(cp.plan_handle) st


join sys.dm_os_memory_cache_entries ce


on cp.memory_object_address = ce.memory_object_address


where cp.cacheobjtype = 'Compiled Plan'


and (cp.objtype = 'Adhoc' or cp.objtype = 'Prepared')


order by cp.objtype desc, cp.usecounts desc


go



select st.text, cp.objtype, cp.refcounts, cp.usecounts, cp.size_in_bytes,


ce.disk_ios_count, ce.context_switches_count,


ce.pages_allocated_count, ce.original_cost, ce.current_cost


from sys.dm_exec_cached_plans cp


cross apply sys.dm_exec_sql_text(cp.plan_handle) st


join sys.dm_os_memory_cache_entries ce


on cp.memory_object_address = ce.memory_object_address


where cp.cacheobjtype = 'Compiled Plan'


and ce.type in ('CACHESTORE_SQLCP', 'CACHESTORE_OBJCP')


order by cp.objtype desc, cp.usecounts desc


go




  1. To estimate the amount of plan cache memory that is being reused use:


select sum(size_in_bytes)/1000 as total_size_in_KB,


count(size_in_bytes) as number_of_plans,


((sum(size_in_bytes)/1000) / (count(size_in_bytes))) as avg_size_in_KB,


cacheobjtype, usecounts


from sys.dm_exec_cached_plans


group by  usecounts, cacheobjtype


order by usecounts asc


go



The total_size_in_KB with usecounts > 1 represents the plan cache memory that is being reused.




  1. To estimate the amount of memory that can be reclaimed after the next round of memory pressure use:


select ce.type, ce.current_cost,


sum(cp.size_in_bytes) as total_size_in_bytes


from sys.dm_exec_cached_plans cp


join sys.dm_os_memory_cache_entries ce


on cp.memory_object_address=ce.memory_object_address


where ce.type in ('CACHESTORE_SQLCP', 'CACHESTORE_OBJCP') and


ce.current_cost = 0


group by ce.type, ce.current_cost


go



The total_size_in_bytes with current_cost = 0 is the memory that will available after the next round of memory pressure.




  1. The DMV sys.dm_os_memory_cache_clock_hands has information regarding how many clock rounds have been made for each cache store. The query below should return 4 rows, two for each cachestore. Each cachestore has an external and internal clock hand that distinguishes external and internal memory pressure respectively. The column removed_last_round_count indicates the number of entries (plans) removed in the last round, and the removed_all_rounds_count indicates the total number of entries removed.


select * from sys.dm_os_memory_cache_clock_hands


where type = 'CACHESTORE_SQLCP' or type = 'CACHESTORE_OBJCP'


go




  1. The DMV sys.dm_os_memory_cache_counters has information on the single and multi-page allocations made for the SQLCP and OBJCP cachestores. A large number of multi_pages_kb can lead to a performance slowdown without SQL Server 2005 SP2.


select *, name, type, single_pages_kb, multi_pages_kb,


single_pages_in_use_kb, multi_pages_in_use_kb


from sys.dm_os_memory_cache_counters


where type = 'CACHESTORE_SQLCP' or type = 'CACHESTORE_OBJCP'


go



Alternatively, query sys.dm_os_memory_clerks to get the memory allocation information along with the memory_node_id:



select type, name, memory_node_id, single_pages_kb, multi_pages_kb


from sys.dm_os_memory_clerks


where type = 'CACHESTORE_SQLCP' or type = 'CACHESTORE_OBJCP'


go




  1. The DMV sys.dm_os_memory_cache_hash_tables has information on the hash bucket length for SQLCP and OBJCP cachestores. A large value for buckets_average_length and a small value for buckets_in_use_count indicate long chains in each hash bucket. Long hash bucket lengths can lead to performance slowdown.



select name, type, buckets_count, buckets_in_use_count,


buckets_min_length, buckets_max_length, buckets_avg_length


from sys.dm_os_memory_cache_hash_tables


where type = 'CACHESTORE_SQLCP' or type = 'CACHESTORE_OBJCP'


go



Note that by removing the where clause in queries 5 though 7 we can get information for


other cachestores also.




  1. If the size of the plan cache is over a few GB use the following queries:


To get a count of the number of compiled plans use:



select count(*) from sys.dm_Exec_Cached_plans


where cacheobjtype = 'Compiled Plan'



To get a count of the number of adhoc query plans use:



select count(*) from sys.dm_Exec_Cached_plans


where cacheobjtype = 'Compiled Plan'


and objtype = 'Adhoc'



To get a count of the number of prepared query plans use:



select count(*) from sys.dm_Exec_Cached_plans


where cacheobjtype = 'Compiled Plan'


and objtype = 'Prepared'



For the number of prepared query plans with a given usecount use:



select usecounts, count(*) as no_of_plans


from sys.dm_Exec_Cached_plans


where cacheobjtype = 'Compiled Plan'


and objtype = 'Prepared'


group by usecounts



For the number of adhoc query plans with a given usecount use:



select usecounts, count(*) as no_of_plans


from sys.dm_Exec_Cached_plans


where cacheobjtype = 'Compiled Plan'


and objtype = 'Adhoc'


group by usecounts



For the top 1000 adhoc compiled plans with usecount of 1 use:



select top(1000) * from sys.dm_Exec_cached_plans


cross apply sys.dm_exec_sql_text(plan_handle)


where cacheobjtype = 'Compiled Plan'


and objtype = 'Adhoc' and usecounts = 1





  1. To take a full dump turn on T2544 and T8004 (trigger a dump when the lazy writer cannot provide enough free buffers) and execute: dbcc stackdump(0)
Version history
Last update:
‎Mar 23 2019 04:50 AM
Updated by: