Blog Post

Azure Database Support Blog
2 MIN READ

Lesson Learned #442: Determining CPU Usage in Azure SQL Database: One Query or Many?

Jose_Manuel_Jurado's avatar
Oct 12, 2023

When managing Azure SQL Databases, it's crucial to monitor performance metrics, especially CPU usage. One of the challenges faced by database administrators is determining whether high CPU usage is caused by a single intensive query or a combination of many. This distinction can help streamline optimization processes, making databases more efficient. In this article, we'll dive into a technique that provides a clear ranking of queries based on CPU consumption.

 

The Issue at Hand:

Our customer asked: they wanted to understand if a single query was monopolizing the CPU or if many queries collectively contributed to high CPU usage. Knowing this can significantly influence the direction of optimization efforts. A single problematic query might require rewriting or indexing, whereas multiple queries causing high CPU usage could indicate broader architectural or schema-related issues.

 

The Technical Approach:

Azure SQL Database offers Dynamic Management Views (DMVs) that provide a wealth of information on server state and performance metrics. We turned to sys.dm_exec_query_stats, which offers insights into the performance of cached query plans.

 

Building the Query:

1. Capturing Individual Query Statistics:
- Our first step was to group data by sql_handle and plan_handle. This allows us to accumulate the total worker time (essentially CPU time) and execution count for each distinct query.

 

SELECT
    qs.sql_handle,
    qs.plan_handle,
    qs.execution_count AS Execution_Count,
    Total_CPU_Time = SUM(qs.total_worker_time)
FROM
    sys.dm_exec_query_stats qs
GROUP BY
    qs.sql_handle, qs.plan_handle, qs.execution_count

2. Ranking the Queries:
- We ranked each query based on its total CPU time, making it easier to identify the top contributors.

 

SELECT
    ROW_NUMBER() OVER (ORDER BY Total_CPU_Time DESC) as Query_Rank,
    QueryCPU.*,
    t.text AS SQL_Text
FROM
    QueryCPU
    CROSS APPLY sys.dm_exec_sql_text(QueryCPU.sql_handle) AS t

3. Calculating the Overall CPU Time:
- To understand the proportion of CPU time each query consumes, we need the total CPU time for all queries.
- SQL code:

SELECT SUM(Total_CPU_Time) as GrandTotal_CPU_Time FROM QueryCPU

 

4. Bringing It All Together:
- Finally, we combined the ranked queries with the total CPU time to calculate each query's percentage of the total CPU usage.
- SQL code:

SELECT TOP 50
    r.Query_Rank,
    r.Total_CPU_Time,
    r.Execution_Count,
    r.SQL_Text,
    (CAST(r.Total_CPU_Time AS FLOAT) / t.GrandTotal_CPU_Time) * 100 as Percentage_Of_Total_CPU
FROM
    RankedQueries r, TotalCPU t
ORDER BY
    r.Query_Rank;

 

Conclusion:

 

The output provides a clear ranking of the top 50 queries based on CPU consumption, as well as the percentage of the total CPU time they represent. This insight allows database administrators to determine whether the high CPU usage is due to a single query or multiple ones.

It's essential to remember that this analysis is just a starting point. Depending on the database's state, additional measures like checking for missing indexes, rewriting inefficient queries, or reconsidering the database design might be necessary. Nonetheless, understanding the CPU consumption pattern is a critical step in the optimization journey.

Published Oct 12, 2023
Version 1.0