Query performance troubleshooting in SQL Server 2008: query_hash and query_plan_hash

 

Recently I have noticed 2 new columns added to sys.dm_exec_query_stats and sys.dm_exec_requests DMVs in SQL Server 2008: query_hash and query_plan_hash. Those columns can greatly enhance performance monitoring process. In SQL 2005 main query I'm using for query performance monitoring is:

SELECT TOP 10
   qs.execution_count
,
   (
qs.total_physical_reads + qs.total_logical_reads + qs.total_logical_writes) AS [Total IO],
   (
qs.total_physical_reads + qs.total_logical_reads + qs.total_logical_writes) /qs.execution_count AS [Avg IO],
  
SUBSTRING(qt.[text], qs.statement_start_offset/2, (
      
CASE 
          
WHEN qs.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.[text])) *
          
ELSE qs.statement_end_offset 
      
END - qs.statement_start_offset)/
  
) AS query_text,
  
qt.[dbid],
  
qt.objectid,
  
tp.query_plan
FROM 
  
sys.dm_exec_query_stats qs
  
CROSS APPLY sys.dm_exec_sql_text (qs.[sql_handle]) AS qt
  
OUTER APPLY sys.dm_exec_query_plan(qs.plan_handle) tp
ORDER BY [Avg IO] DESC
--ORDER BY [Total IO] DESC

It returns top 10 heaviest queries by average IO that exist in cache. I won't go deeper discussing plan cache memory pressure conditions that can force query plan out of cache thus preventing its detection. Let's just say that it works right in 99% of cases which is good enough. Memory pressure can be detected by other queries.

Which queries would be candidates for tuning? First, those with highest total IO. Second, queries with highest average IO that pass certain minimum number of executions criteria (we usually won't tune query, even the heaviest one, that runs once a month in some offline batch). As a side node - you're probably asking, why do I ignore CPU counters like total_worker_time. The reason is simple: in sys.dm_exec_query_stats this counter is unreliable. It shows incorrect numbers in case of parallel execution.

So, Houston,  do we have a problem here? Unfortunately, we do - when application that works with the database doesn't make proper usage of parameterization (and we don't want to force parameterization via database level setting). In such a case we'll see lots of similar queries with 1 or 2 in execution_count and different values of should-be-parameters in query_text. We can miss such queries because every single one is not heavy enough to be of interest or because many of the queries aren't in cache anymore pushed out by new queries - even of the same type. It is especially realistic scenario for 32 bit systems where entire non-data cache is limited to 1GB of space.

What are our options with poorly parameterized queries? We can use CLR user-defined function provided by Itzik Ben-Gan in his book "Inside SQL Server 2005: T-SQL Querying" that uses regular expressions functionality in order to parameterize query text (this function is widely used for Profiler trace analysis). Query text can be passed through the function and used as grouping column. But even if we don't count performance and CPU price of grouping by text column, I know several organizations that just won't let you create your objects in their database.

Taking all the above into account, I was delighted to find out that in SQL 2008 Microsoft added query_hash and query_plan_hash columns to sys.dm_exec_query_stats DMV. query_hash would be the same for queries with similar logic, query_plan_hash would be the same for queries with similar execution plan. And what's the difference? For column with uneven data distribution, execution plan can be different depending on parameter value. If we have the same value in 90% of a table, Optimizer would sure choose scan option. For another value which is responsible for 0.1% of rows Optimizer will prefer index seek with key or RID lookup depending on table's structure. For those two queries we'll see the same query_hash but different query_plan_hash.

So new SQL 2008 version of the query is:

;WITH CTE(TotalExecutions, [Total IO], [Avg IO], StatementTextForExample, plan_handle, QueyHash, QueryPlanHash)
AS
(
  
SELECT TOP 10  
      
SUM(execution_count) AS TotalExecutions,
      
SUM(total_physical_reads + total_logical_reads + total_logical_writes) AS [Total IO],
      
SUM(total_physical_reads + total_logical_reads + total_logical_writes) / SUM(execution_count) AS [Avg IO],
      
MIN(query_text) AS StatementTextForExample,
      
MIN(plan_handle) AS plan_handle,
      
query_hash AS QueryHash,
      
query_plan_hash AS QueryPlanHash
  
FROM 
      
(
          
SELECT 
              
qs.*, 
              
SUBSTRING(qt.[text], qs.statement_start_offset/2, (
                  
CASE 
                      
WHEN qs.statement_end_offset = -1 THEN LEN(CONVERT(NVARCHAR(MAX), qt.[text])) *
                      
ELSE qs.statement_end_offset 
                  
END - qs.statement_start_offset)/
              
) AS query_text
          
FROM 
              
sys.dm_exec_query_stats AS qs
              
CROSS APPLY sys.dm_exec_sql_text(qs.[sql_handle]) AS qt
          
WHERE qt.[text] NOT LIKE '%sys.dm_exec_query_stats%'
      
) AS query_stats
  
GROUP BY query_hash, query_plan_hash
  
ORDER BY [Avg IO] DESC
)
SELECT 
  
TotalExecutions, [Total IO], [Avg IO],
  
StatementTextForExample,
  
tp.query_plan AS StatementPlan,
  
QueyHash, QueryPlanHash
FROM
  
CTE
  
OUTER APPLY sys.dm_exec_query_plan(plan_handle) AS tp
ORDER BY [Avg IO] DESC;
--ORDER BY [Total IO] DESC;

Another great usage of new columns is to create repository and monitor execution plan changes over time, i.e. changes in query_plan_hash for the queries with the same query_hash value. In SQL 2005 such monitoring was pretty complicated and required on-the-fly parameterization. In SQL 2008 it looks pretty straightforward.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值