patternsqlMinor
Poor Performance Running dm_exec_query_stats
Viewed 0 times
runningperformancedm_exec_query_statspoor
Problem
I have a job that executes every 5 minutes to gather info from DMVs that has not changed in over 2 years, normally running under a minute.
All of a sudden starting last week, with no changes deployed, this job started running over 12 minutes on only 1 server out of 15. Looking at the execution plan nothing has changed, but the reads, writes, CPU time, and rowcounts all went up by an order of magnitude. The number of rows inserted hasn't changed by much either.
Could this be bad statistics in the underlying system tables? If so, how can it be solved, because I can't run UPDATE STATISTICS on any of the DMVs which just give a message about the object not existing.
Here is the code in question:
```
SELECT
ISNULL(DB_NAME(st.dbid), 'NULL') AS [DatabaseName]
,ISNULL(OBJECT_SCHEMA_NAME(st.objectid, st.dbid), 'NULL') AS [SchemaName]
,ISNULL(OBJECT_NAME(st.objectid, st.dbid), 'NULL') AS [ObjectName]
,cp.objtype AS [ObjectType]
,qs.statement_start_offset AS [StatementStartOffset]
,qs.statement_end_offset AS [StatementEndOffset]
,qs.query_hash AS [QueryHash]
,qs.query_plan_hash AS [QueryPlanHash]
,qs.sql_handle AS [SQLHandle]
,qs.plan_handle AS [PlanHandle]
,qs.plan_generation_num AS [PlanGenerationNumber]
,cp.usecounts AS [UseCounts]
,cp.refcounts AS [RefCounts]
-- find the offset of the actual statement being executed
,SUBSTRING(st.text,
CASE
WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1
ELSE qs.statement_start_offset/2 + 1
END,
CASE
WHEN qs.statement_end_offset = 0 OR qs.statement_end_offset = -1 OR qs.statement_end_offset IS NULL THEN LEN(st.text)
ELSE qs.statement_end_offset/2
END -
CASE
WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1
ELSE qs.statement_start_offset/2
All of a sudden starting last week, with no changes deployed, this job started running over 12 minutes on only 1 server out of 15. Looking at the execution plan nothing has changed, but the reads, writes, CPU time, and rowcounts all went up by an order of magnitude. The number of rows inserted hasn't changed by much either.
Could this be bad statistics in the underlying system tables? If so, how can it be solved, because I can't run UPDATE STATISTICS on any of the DMVs which just give a message about the object not existing.
Here is the code in question:
```
SELECT
ISNULL(DB_NAME(st.dbid), 'NULL') AS [DatabaseName]
,ISNULL(OBJECT_SCHEMA_NAME(st.objectid, st.dbid), 'NULL') AS [SchemaName]
,ISNULL(OBJECT_NAME(st.objectid, st.dbid), 'NULL') AS [ObjectName]
,cp.objtype AS [ObjectType]
,qs.statement_start_offset AS [StatementStartOffset]
,qs.statement_end_offset AS [StatementEndOffset]
,qs.query_hash AS [QueryHash]
,qs.query_plan_hash AS [QueryPlanHash]
,qs.sql_handle AS [SQLHandle]
,qs.plan_handle AS [PlanHandle]
,qs.plan_generation_num AS [PlanGenerationNumber]
,cp.usecounts AS [UseCounts]
,cp.refcounts AS [RefCounts]
-- find the offset of the actual statement being executed
,SUBSTRING(st.text,
CASE
WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1
ELSE qs.statement_start_offset/2 + 1
END,
CASE
WHEN qs.statement_end_offset = 0 OR qs.statement_end_offset = -1 OR qs.statement_end_offset IS NULL THEN LEN(st.text)
ELSE qs.statement_end_offset/2
END -
CASE
WHEN qs.statement_start_offset = 0 OR qs.statement_start_offset IS NULL THEN 1
ELSE qs.statement_start_offset/2
Solution
Nick Craver ran into performance issues with this too in Opserver. Here's the query he ended up with after working directly with MS folks on the query plan - the joins look kinda contorted, but as I recall, that was the only way he could get consistently good performance and avoid the timeout issues:
SELECT AvgCPU, AvgDuration, AvgReads, AvgCPUPerMinute,
TotalCPU, TotalDuration, TotalReads,
PercentCPU, PercentDuration, PercentReads, PercentExecutions,
ExecutionCount,
ExecutionsPerMinute,
PlanCreationTime, LastExecutionTime,
SUBSTRING(st.text,
(StatementStartOffset / 2) + 1,
((CASE StatementEndOffset
WHEN -1 THEN DATALENGTH(st.text)
ELSE StatementEndOffset
END - StatementStartOffset) / 2) + 1) AS QueryText,
st.Text FullText,
query_plan AS QueryPlan,
PlanHandle,
StatementStartOffset,
StatementEndOffset,
MinReturnedRows,
MaxReturnedRows,
AvgReturnedRows,
TotalReturnedRows,
LastReturnedRows,
DB_NAME(DatabaseId) AS CompiledOnDatabase
FROM (SELECT TOP (@MaxResultCount)
total_worker_time / execution_count AS AvgCPU,
total_elapsed_time / execution_count AS AvgDuration,
total_logical_reads / execution_count AS AvgReads,
Cast(total_worker_time / age_minutes As BigInt) AS AvgCPUPerMinute,
execution_count / age_minutes AS ExecutionsPerMinute,
Cast(total_worker_time / age_minutes_lifetime As BigInt) AS AvgCPUPerMinuteLifetime,
execution_count / age_minutes_lifetime AS ExecutionsPerMinuteLifetime,
total_worker_time AS TotalCPU,
total_elapsed_time AS TotalDuration,
total_logical_reads AS TotalReads,
execution_count ExecutionCount,
CAST(ROUND(100.00 * total_worker_time / t.TotalWorker, 2) AS MONEY) AS PercentCPU,
CAST(ROUND(100.00 * total_elapsed_time / t.TotalElapsed, 2) AS MONEY) AS PercentDuration,
CAST(ROUND(100.00 * total_logical_reads / t.TotalReads, 2) AS MONEY) AS PercentReads,
CAST(ROUND(100.00 * execution_count / t.TotalExecs, 2) AS MONEY) AS PercentExecutions,
qs.creation_time AS PlanCreationTime,
qs.last_execution_time AS LastExecutionTime,
qs.plan_handle AS PlanHandle,
qs.statement_start_offset AS StatementStartOffset,
qs.statement_end_offset AS StatementEndOffset,
qs.min_rows AS MinReturnedRows,
qs.max_rows AS MaxReturnedRows,
CAST(qs.total_rows as MONEY) / execution_count AS AvgReturnedRows,
qs.total_rows AS TotalReturnedRows,
qs.last_rows AS LastReturnedRows,
qs.sql_handle AS SqlHandle,
Cast(pa.value as Int) DatabaseId
FROM (SELECT *,
CAST((CASE WHEN DATEDIFF(second, creation_time, GETDATE()) > 0 And execution_count > 1
THEN DATEDIFF(second, creation_time, GETDATE()) / 60.0
ELSE Null END) as MONEY) as age_minutes,
CAST((CASE WHEN DATEDIFF(second, creation_time, last_execution_time) > 0 And execution_count > 1
THEN DATEDIFF(second, creation_time, last_execution_time) / 60.0
ELSE Null END) as MONEY) as age_minutes_lifetime
FROM sys.dm_exec_query_stats) AS qs
CROSS JOIN(SELECT SUM(execution_count) TotalExecs,
SUM(total_elapsed_time) TotalElapsed,
SUM(total_worker_time) TotalWorker,
SUM(total_logical_reads) TotalReads
FROM sys.dm_exec_query_stats) AS t
CROSS APPLY sys.dm_exec_plan_attributes(qs.plan_handle) AS pa
WHERE pa.attribute = 'dbid'
{0}) sq
CROSS APPLY sys.dm_exec_sql_text(SqlHandle) AS st
CROSS APPLY sys.dm_exec_query_plan(PlanHandle) AS qpCode Snippets
SELECT AvgCPU, AvgDuration, AvgReads, AvgCPUPerMinute,
TotalCPU, TotalDuration, TotalReads,
PercentCPU, PercentDuration, PercentReads, PercentExecutions,
ExecutionCount,
ExecutionsPerMinute,
PlanCreationTime, LastExecutionTime,
SUBSTRING(st.text,
(StatementStartOffset / 2) + 1,
((CASE StatementEndOffset
WHEN -1 THEN DATALENGTH(st.text)
ELSE StatementEndOffset
END - StatementStartOffset) / 2) + 1) AS QueryText,
st.Text FullText,
query_plan AS QueryPlan,
PlanHandle,
StatementStartOffset,
StatementEndOffset,
MinReturnedRows,
MaxReturnedRows,
AvgReturnedRows,
TotalReturnedRows,
LastReturnedRows,
DB_NAME(DatabaseId) AS CompiledOnDatabase
FROM (SELECT TOP (@MaxResultCount)
total_worker_time / execution_count AS AvgCPU,
total_elapsed_time / execution_count AS AvgDuration,
total_logical_reads / execution_count AS AvgReads,
Cast(total_worker_time / age_minutes As BigInt) AS AvgCPUPerMinute,
execution_count / age_minutes AS ExecutionsPerMinute,
Cast(total_worker_time / age_minutes_lifetime As BigInt) AS AvgCPUPerMinuteLifetime,
execution_count / age_minutes_lifetime AS ExecutionsPerMinuteLifetime,
total_worker_time AS TotalCPU,
total_elapsed_time AS TotalDuration,
total_logical_reads AS TotalReads,
execution_count ExecutionCount,
CAST(ROUND(100.00 * total_worker_time / t.TotalWorker, 2) AS MONEY) AS PercentCPU,
CAST(ROUND(100.00 * total_elapsed_time / t.TotalElapsed, 2) AS MONEY) AS PercentDuration,
CAST(ROUND(100.00 * total_logical_reads / t.TotalReads, 2) AS MONEY) AS PercentReads,
CAST(ROUND(100.00 * execution_count / t.TotalExecs, 2) AS MONEY) AS PercentExecutions,
qs.creation_time AS PlanCreationTime,
qs.last_execution_time AS LastExecutionTime,
qs.plan_handle AS PlanHandle,
qs.statement_start_offset AS StatementStartOffset,
qs.statement_end_offset AS StatementEndOffset,
qs.min_rows AS MinReturnedRows,
qs.max_rows AS MaxReturnedRows,
CAST(qs.total_rows as MONEY) / execution_count AS AvgReturnedRows,
qs.total_rows AS TotalReturnedRows,
qs.last_rows AS LastReturnedRows,
qs.sql_handle AS SqlHandle,
Cast(pa.value as Int) DatabaseId
FROM (SELECT *,
CAST((CASE WHEN DATEDIFF(second, creation_time, GETDATE()) > 0 And execution_count > 1
THEN DATEDIFF(second, creation_time, GETDATE()) / 60.0
ELSE Null END) as MONEY) as age_minutes,
CAST((CASE WHEN DATEDIFF(second, creation_time, last_Context
StackExchange Database Administrators Q#202948, answer score: 4
Revisions (0)
No revisions yet.