Tuesday, April 22, 2014

Wait Stats in SQL Server

/*
SQL Server Wait Information from sys.dm_os_wait_stats
Copyright (C) 2014, Brent Ozar Unlimited.
See http://BrentOzar.com/go/eula for the End User Licensing Agreement.
*/

/*********************************
Let's build a list of waits we can safely ignore.
*********************************/
IF OBJECT_ID('tempdb..#ignorable_waits') IS NOT NULL
    DROP TABLE #ignorable_waits;
GO

create table #ignorable_waits (wait_type nvarchar(256) PRIMARY KEY);
GO

/* We aren't using row constructors to be SQL 2005 compatible */
set nocount on;
insert #ignorable_waits (wait_type) VALUES ('REQUEST_FOR_DEADLOCK_SEARCH');
insert #ignorable_waits (wait_type) VALUES ('SQLTRACE_INCREMENTAL_FLUSH_SLEEP');
insert #ignorable_waits (wait_type) VALUES ('SQLTRACE_BUFFER_FLUSH');
insert #ignorable_waits (wait_type) VALUES ('LAZYWRITER_SLEEP');
insert #ignorable_waits (wait_type) VALUES ('XE_TIMER_EVENT');
insert #ignorable_waits (wait_type) VALUES ('XE_DISPATCHER_WAIT');
insert #ignorable_waits (wait_type) VALUES ('FT_IFTS_SCHEDULER_IDLE_WAIT');
insert #ignorable_waits (wait_type) VALUES ('LOGMGR_QUEUE');
insert #ignorable_waits (wait_type) VALUES ('CHECKPOINT_QUEUE');
insert #ignorable_waits (wait_type) VALUES ('BROKER_TO_FLUSH');
insert #ignorable_waits (wait_type) VALUES ('BROKER_TASK_STOP');
insert #ignorable_waits (wait_type) VALUES ('BROKER_EVENTHANDLER');
insert #ignorable_waits (wait_type) VALUES ('SLEEP_TASK');
insert #ignorable_waits (wait_type) VALUES ('WAITFOR');
insert #ignorable_waits (wait_type) VALUES ('DBMIRROR_DBM_MUTEX')
insert #ignorable_waits (wait_type) VALUES ('DBMIRROR_EVENTS_QUEUE')
insert #ignorable_waits (wait_type) VALUES ('DBMIRRORING_CMD');
insert #ignorable_waits (wait_type) VALUES ('DISPATCHER_QUEUE_SEMAPHORE');
insert #ignorable_waits (wait_type) VALUES ('BROKER_RECEIVE_WAITFOR');
insert #ignorable_waits (wait_type) VALUES ('CLR_AUTO_EVENT');
insert #ignorable_waits (wait_type) VALUES ('DIRTY_PAGE_POLL');
insert #ignorable_waits (wait_type) VALUES ('HADR_FILESTREAM_IOMGR_IOCOMPLETION');
insert #ignorable_waits (wait_type) VALUES ('ONDEMAND_TASK_QUEUE');
insert #ignorable_waits (wait_type) VALUES ('FT_IFTSHC_MUTEX');
insert #ignorable_waits (wait_type) VALUES ('CLR_MANUAL_EVENT');
insert #ignorable_waits (wait_type) VALUES ('SP_SERVER_DIAGNOSTICS_SLEEP');
insert #ignorable_waits (wait_type) VALUES ('QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP');
insert #ignorable_waits (wait_type) VALUES ('QDS_PERSIST_TASK_MAIN_LOOP_SLEEP');
GO

/* Want to manually exclude an event and recalculate?*/
/* insert #ignorable_waits (wait_type) VALUES (''); */

/*********************************
What are the highest overall waits since startup?
*********************************/
SELECT TOP 25
 os.wait_type,
 SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
 CAST(
  100.* SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type)
  / (1. * SUM(os.wait_time_ms) OVER () )
  AS NUMERIC(12,1)) as pct_wait_time,
 SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks,
 CASE WHEN  SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) > 0
 THEN
  CAST(
   SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type)
    / (1. * SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type))
   AS NUMERIC(12,1))
 ELSE 0 END AS avg_wait_time_ms,
 CURRENT_TIMESTAMP as sample_time
FROM sys.dm_os_wait_stats os
LEFT JOIN #ignorable_waits iw on
 os.wait_type=iw.wait_type
WHERE
 iw.wait_type is null
ORDER BY sum_wait_time_ms DESC;
GO

/*********************************
What are the higest waits *right now*?
*********************************/

/* Note: this is dependent on the #ignorable_waits table created earlier. */
if OBJECT_ID('tempdb..#wait_batches') is not null
 drop table #wait_batches;
if OBJECT_ID('tempdb..#wait_data') is not null
 drop table #wait_data;
GO

CREATE TABLE #wait_batches (
 batch_id int identity primary key,
 sample_time datetime not null
);
CREATE TABLE #wait_data
    ( batch_id INT NOT NULL ,
      wait_type NVARCHAR(256) NOT NULL ,
      wait_time_ms BIGINT NOT NULL ,
      waiting_tasks BIGINT NOT NULL
    );
CREATE CLUSTERED INDEX cx_wait_data on #wait_data(batch_id);
GO

/*
This temporary procedure records wait data to a temp table.
*/
if OBJECT_ID('tempdb..#get_wait_data') IS NOT NULL
 DROP procedure #get_wait_data;
GO
CREATE PROCEDURE #get_wait_data
 @intervals tinyint = 2,
 @delay char(12)='00:00:30.000' /* 30 seconds*/
AS
DECLARE @batch_id int,
 @current_interval tinyint,
 @msg nvarchar(max);

SET NOCOUNT ON;
SET @current_interval=1;

WHILE @current_interval <= @intervals
BEGIN
 INSERT #wait_batches(sample_time)
 SELECT CURRENT_TIMESTAMP;

 SELECT @batch_id=SCOPE_IDENTITY();

 INSERT #wait_data (batch_id, wait_type, wait_time_ms, waiting_tasks)
 SELECT
  @batch_id,
  os.wait_type,
  SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
  SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks
 FROM sys.dm_os_wait_stats os
 LEFT JOIN #ignorable_waits iw on
  os.wait_type=iw.wait_type
 WHERE
  iw.wait_type is null
 ORDER BY sum_wait_time_ms DESC;

 set @msg= CONVERT(char(23),CURRENT_TIMESTAMP,121)+ N': Completed sample '
  + cast(@current_interval as nvarchar(4))
  + N' of ' + cast(@intervals as nvarchar(4)) +
  '.'
 RAISERROR (@msg,0,1) WITH NOWAIT;

 SET @current_interval=@current_interval+1;

 if @current_interval <= @intervals
  WAITFOR DELAY @delay;
END
GO

/*
Let's take two samples 30 seconds apart
*/
exec #get_wait_data @intervals=2, @delay='00:00:30.000';
GO

/*
What were we waiting on?
This query compares the most recent two samples.
*/
with max_batch as (
 select top 1 batch_id, sample_time
 from #wait_batches
 order by batch_id desc
)
SELECT
 b.sample_time as [Second Sample Time],
 datediff(ss,wb1.sample_time, b.sample_time) as [Sample Duration in Seconds],
 wd1.wait_type,
 cast((wd2.wait_time_ms-wd1.wait_time_ms)/1000. as numeric(12,1)) as [Wait Time (Seconds)],
 (wd2.waiting_tasks-wd1.waiting_tasks) AS [Number of Waits],
 CASE WHEN (wd2.waiting_tasks-wd1.waiting_tasks) > 0
 THEN
  cast((wd2.wait_time_ms-wd1.wait_time_ms)/
   (1.0*(wd2.waiting_tasks-wd1.waiting_tasks)) as numeric(12,1))
 ELSE 0 END AS [Avg ms Per Wait]
FROM  max_batch b
JOIN #wait_data wd2 on
 wd2.batch_id=b.batch_id
JOIN #wait_data wd1 on
 wd1.wait_type=wd2.wait_type AND
 wd2.batch_id - 1 = wd1.batch_id
join #wait_batches wb1 on
 wd1.batch_id=wb1.batch_id
WHERE (wd2.waiting_tasks-wd1.waiting_tasks) > 0
ORDER BY [Wait Time (Seconds)] DESC;
GO




/*
For a description of each wait see: http://msdn.microsoft.com/en-us/library/ms179984






source: http://www.brentozar.com/responder/triage-wait-stats-in-sql-server/

*/

No comments:

Post a Comment