SQL SERVER – Inspecting Historical CPU Ring Buffer Samples

CPU Usage History helps me establish when a reported problem began. It gives a timeline rather than identifying the responsible query.

A sequence of glaze samples recedes along a rack beside an inspection lens.

The query reads scheduler-monitor XML from an in-memory ring buffer. I limit it to one hour for the older DATEADD integer range. The timestamp is approximate, based on the current server clock.

DECLARE @ticks_ms bigint = (SELECT ms_ticks FROM sys.dm_os_sys_info);
SELECT TOP (60) id,
    DATEADD(millisecond, CONVERT(int, [timestamp] - @ticks_ms), GETDATE()) AS EventTime,
    ProcessUtilization AS SQL_CPU_percent,
    SystemIdle AS Idle_CPU_percent,
    100 - SystemIdle - ProcessUtilization AS Other_CPU_percent
FROM
(
    SELECT record.value('(./Record/@id)[1]', 'int') AS id,
        record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]', 'int') AS SystemIdle,
        record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]', 'int') AS ProcessUtilization,
        [timestamp]
    FROM
    (
        SELECT [timestamp], CONVERT(xml, record) AS record
        FROM sys.dm_os_ring_buffers
        WHERE ring_buffer_type = N'RING_BUFFER_SCHEDULER_MONITOR'
          AND record LIKE '%<SystemHealth>%'
          AND [timestamp] >= @ticks_ms - 3600000
    ) AS buffer
) AS health
ORDER BY [timestamp] DESC;

SQL_CPU_percent is the SQL Server process utilization reported in that sample. Other_CPU_percent is the remainder after SQL Server and idle time. These are historical samples, not a continuously recorded Windows performance counter or a permanent monitoring archive. Ring buffers overwrite older records and are lost across restarts.

Original CPU-history result from the 2016 demonstration. These values are historical, not a measurement of your server.
Original CPU-history result from the 2016 demonstration. These values are historical, not a measurement of your server.

I generated the historical picture with a large cross join in three connections. I wouldn’t repeat that load on a customer server. Instead, I correlate the timeline with workload and waits. The internal XML format and required permissions depend on the SQL Server version.

Reference: Ring-buffer scope and permissions.

A CPU sample is not a query diagnosis, it is a time-based clue to investigate.

Published by Pinal Dave on SQLAuthority. More of my work at pinaldave.com.


Discover more from SQL Authority with Pinal Dave

Subscribe to get the latest posts sent to your email.

SQL CPU, SQL Scripts, SQL Server
Previous Post
SQL SERVER 2016 – Encrypt Your PII Data – Notes from the Field #132
Next Post
What Breaks If I Drop This Column? sys.sql_expression_dependencies

Related Posts

13 Comments. Leave new

  • I use the DMV to identify the query which kept the highest CPU more than 3 minutes within SQL Server. The Performance Analysis of logs (PAL) does also help out to find out the resources issue.

    Reply
  • This DMV would sometimes give inaccurate result where CPU utilization is more than 100 ;-)
    I am still looking for an alternative for this as this has been working for long time until recently.
    Please let us know if you have one in mind. Thanks.

    Reply
  • The query does not return correct result where the value can be negative … I hope SP1 would fix sys.dm_os_ring_buffers

    Reply
  • select highest_cpu_queries.plan_handle,highest_cpu_queries.
    plan_generation_num,highest_cpu_queries.max_worker_time,
    highest_cpu_queries.total_physical_reads,
    highest_cpu_queries.total_logical_reads,
    highest_cpu_queries.total_elapsed_time,q.[text],q.dbid,q.objectid,q.number,q.encrypted,query_plan
    from (select top 50 qs.plan_handle,
    qs.plan_generation_num,qs.creation_time, qs.execution_count, qs.total_worker_time,
    qs.max_worker_time, qs.total_elapsed_time,
    qs.max_elapsed_time, qs.total_logical_reads, qs.max_logical_reads,
    qs.total_physical_reads, qs.max_physical_reads from sys.dm_exec_query_stats
    qs order by qs.total_worker_time DESC)
    as highest_cpu_queries
    cross apply sys.dm_exec_sql_text (plan_handle) as q
    cross apply sys.dm_exec_query_plan (plan_handle) as qp
    order by highest_cpu_queries.total_worker_time DESC

    Reply
  • Is it possible to get current SQL server CPU usage through a query ?

    Reply
  • hello sir,
    good evening, I am Lokesh (working as SQL-DBA L3). Sir, I want to know that what is meaning of 100 to count the syscpu i.e. in above SQL
    “100 – SystemIdle – ProcessUtilization AS ‘Others (100-SQL-Idle)'” .
    why we are using 100.
    in my system I have 56 CPU i.e. 112 cores and applied this script but getting syscpu < sqlcpu, which is wrong. please help me out.

    Regards
    Lokesh Kumar

    Reply
  • I also have a same issue, my server has 192 cores but when I run this query it’s appear wrong figures.

    Reply
  • Mitchell McClure
    April 28, 2023 12:48 am

    sys.dm_os_ring_buffers only stores the last 256 minutes of data. Is there a different location where several days of cpu performance data is stored?

    Reply
  • Hello sir
    Good day
    As checked above quary show latest information. But I need to get information before date (like before 4 days before information need ) any query for that information

    Please post that quary

    Reply

Leave a Reply

Your email address will not be published. Required fields are marked *

Fill out this field
Fill out this field
Please enter a valid email address.