SQL Server – Get CPU Usage History for last 256 minutes

The other day, I was asked by our Application Team Lead to check why the CPU usage is very high in the Production DB server. It remained consistently high throughout the day. He asked whether it is coming from SQL Server or other processes.

 

 

Below is a useful script to review the CPU usage history for last 256 minutes (This will work on SQL Server 2008 and up)​​ 

 

 

DECLARE​​ @ts_now​​ bigint​​ =​​ (SELECT​​ cpu_ticks/(cpu_ticks/ms_ticks)​​ FROM​​ sys.dm_os_sys_info​​ WITH​​ (NOLOCK));​​ 

 

SELECT​​ TOP(256)​​ SQLProcessUtilization​​ AS​​ [SQL Server Process CPU Utilization],​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ SystemIdle​​ AS​​ [System Idle Process],​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ 100​​ -​​ SystemIdle​​ -​​ SQLProcessUtilization​​ AS​​ [Other Process CPU Utilization],​​ 

 ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​ ​​​​ DATEADD(ms,​​ -1​​ *​​ (@ts_now​​ -​​ [timestamp]),​​ GETDATE())​​ AS​​ [Event Time]​​ 

FROM​​ (​​ 

  ​​​​ SELECT​​ record.value('(./Record/@id)[1]',​​ 'int')​​ AS​​ record_id,​​ 

 record.value('(./Record/SchedulerMonitorEvent/SystemHealth/SystemIdle)[1]',​​ 'int')​​ 

AS​​ [SystemIdle],​​ 

 record.value('(./Record/SchedulerMonitorEvent/SystemHealth/ProcessUtilization)[1]',​​ 

'int')​​ 

AS​​ [SQLProcessUtilization],​​ [timestamp]​​ 

  ​​​​ FROM​​ (​​ 

SELECT​​ [timestamp],​​ CONVERT(xml,​​ record)​​ AS​​ [record]​​ 

FROM​​ sys.dm_os_ring_buffers​​ WITH​​ (NOLOCK)

WHERE​​ ring_buffer_type​​ =​​ N'RING_BUFFER_SCHEDULER_MONITOR'​​ 

AND​​ record​​ LIKE​​ N'%<SystemHealth>%')​​ AS​​ x​​ 

  ​​​​ )​​ AS​​ y​​ 

ORDER​​ BY​​ record_id​​ DESC​​ OPTION​​ (RECOMPILE);

 

 

Now, if the value in “Other Process CPU Utilization (%)” column is higher​​ than​​ “SQL Server Process CPU”, then you may​​ ask​​ your sys admins to investigate the cause. But in our case, it​​ was indicated​​ clearly that the cause of high CPU usage was coming from SQL Server Process.​​ Below is the output of the​​ script.

 

 

 

After seeing the results, I investigated from the SQL queries perspective and found out that there are few​​ expensive queries that are​​ causing high CPU usage. ​​ How did I find out? That would be the subject of another blog.​​ 

Knowledge worth sharing...Share on linkedin
Linkedin
Share on facebook
Facebook
Share on google
Google
Share on twitter
Twitter

SQL Server – SQL Server Agent Job History

The other day, I was troubleshooting a database slowness issue when the lead application developer asks me whether or not there are any SQL Jobs running during that time. The T-SQL​​ script below helped me to retrieve that info. This T-SQL script will check the SQL Agent Job history.

 

 

SELECT

​​ j.name​​ AS​​ 'JobName',

​​ run_date,

​​ run_time,

​​ msdb.dbo.agent_datetime(run_date,​​ run_time)​​ AS​​ 'RunDateTime'

FROM​​ msdb.dbo.sysjobs j​​ 

INNER​​ JOIN​​ msdb.dbo.sysjobhistory h​​ 

​​ ON​​ j.job_id​​ =​​ h.job_id​​ 

WHERE​​ j.enabled​​ =​​ 1 ​​ --Only Enabled Jobs

ORDER​​ by​​ JobName,​​ RunDateTime​​ DESC

 

Knowledge worth sharing...Share on linkedin
Linkedin
Share on facebook
Facebook
Share on google
Google
Share on twitter
Twitter

SQL Server – Capturing Blocking Information (Enhanced SQL Script)

When the database that I’m handling was experiencing excessive blockings, below is the script I use too see what are being blocked and which stored procedure is the blocker. Blocking​​ is when one connection needs access to a piece of data and has to wait for another connection’s lock to clear.

  SELECT  blocking.session_id AS blocking_session_id,
         blocked.session_id AS blocked_session_id ,
         waitstats.wait_type AS blocking_resource ,
         waitstats.wait_duration_ms ,
         waitstats.resource_description ,
         DB_NAME(tl.resource_database_id) AS DatabaseName,
         blocked_cache.text AS blocked_text ,
         blocking_cache.text AS blocking_text
       FROM 
         sys.dm_exec_connections AS blocking
         INNER JOIN sys.dm_exec_requests blocked
         ON blocking.session_id = blocked.blocking_session_id
         CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle)
         blocked_cache
         CROSS APPLY sys.dm_exec_sql_text(blocking.most_recent_sql_handle)
         blocking_cache
         INNER JOIN sys.dm_os_waiting_tasks waitstats
         ON waitstats.session_id = blocked.session_id
         INNER JOIN sys.dm_tran_locks tl
         ON tl.lock_owner_address = waitstats.resource_address
WHERE waitstats.wait_duration_ms >= 2000

The above query is okay but we were able to enhanced the script by adding columns to see what objects that are being blocked. Below is the enhanced version of the script.

 ;WITH T1 AS (
         SELECT blocking.session_id AS blocking_session_id ,
                blocked.session_id AS blocked_session_id ,
                waitstats.wait_type AS blocking_resource ,
                waitstats.wait_duration_ms ,
                waitstats.resource_description ,
                DB_NAME(tl.resource_database_id) AS DatabaseName,
                blocked_cache.text AS blocked_text ,
                blocking_cache.text AS blocking_text
        FROM 
                sys.dm_exec_connections AS blocking
                INNER JOIN sys.dm_exec_requests blocked
                ON blocking.session_id = blocked.blocking_session_id
                CROSS APPLY sys.dm_exec_sql_text(blocked.sql_handle)
                blocked_cache
                CROSS APPLY sys.dm_exec_sql_text(blocking.most_recent_sql_handle)
                blocking_cache
	            INNER JOIN sys.dm_os_waiting_tasks waitstats
                ON waitstats.session_id = blocked.session_id
                INNER JOIN sys.dm_tran_locks tl
                ON tl.lock_owner_address = waitstats.resource_address
		WHERE waitstats.wait_duration_ms >= 2000
), T2A AS (     
       SELECT blocking_session_id, resource_description,
       CHARINDEX('hobtid', resource_description) AS StartPos,
             SUBSTRING(resource_description, CHARINDEX('hobtid', resource_description), LEN(resource_description)) AS StartText
      FROM T1
), T2B AS (
      SELECT blocking_session_id, resource_description, StartPos, StartText, SUBSTRING(StartText, 0, CHARINDEX(' ', StartText)) AS hotbid_text
      FROM T2A
), T2C AS (
       SELECT blocking_session_id, resource_description, CAST(SUBSTRING(hotbid_text, CHARINDEX('=', hotbid_text)+1, LEN(hotbid_text)) AS BIGINT) AS hobt_id
       FROM T2B
), T2D AS (
       SELECT T2C.blocking_session_id, T2C.resource_description, OBJECT_SCHEMA_NAME(object_id) AS schema_name, object_name(object_id) as object_name, object_id, partition_id, index_id, partition_number, p.hobt_id, rows
       FROM sys.partitions p INNER JOIN T2C ON p.hobt_id = T2C.hobt_id
)
SELECT GETDATE() AS DateTimeCaptured, T1.blocking_session_id, T1.blocked_session_id, T1.blocking_resource, T1.wait_duration_ms, T1.resource_description, T1.DatabaseName, T1.blocking_text, T1.blocked_text, 
T2D.schema_name + '.' + T2D.object_name AS blocked_object_name, T2D.object_id AS blocked_object_id, T2D.index_id as blocked_index_id, i.name AS blocked_index_name, i.type_desc AS blocked_index_type
FROM T1 INNER JOIN T2D ON T1.blocking_session_id = T2D.blocking_session_id AND T1.resource_description = T2D.resource_description
INNER JOIN sys.indexes i ON i.object_id = T2D.object_id AND i.index_id = T2D.index_id

 

Cheers!

 

 

Knowledge worth sharing...Share on linkedin
Linkedin
Share on facebook
Facebook
Share on google
Google
Share on twitter
Twitter