When a performance issue occurs, SQLTop* will show quick and easy entire
SQL Server Performance on real-time, including waits, performance counters and
sessions.
Source : https://www.brentozar.com/responder/triage-wait-stats-in-sql-server/
Triage Wait Stats in SQL Server
Your SQL Server is online, but it’s incredibly slow. The old way to investigate this was to use
SQL Server Perfmon counters, but the new way is to ask SQL Server what the bottleneck is.
While SQL Server is executing queries, it’s constantly tracking what it’s waiting on. For example, f a query has to wait ten seconds for data to come back from storage, SQL adds ten seconds to its total number of PAGEIOLATCH waits. We can query those running totals by looking at the dynamic management view (DMV) sys.dm_os_wait_stats.
The problem with sys.dm_os_wait_stats is that it’s cumulative – it just keeps adding and adding. If you only want to see what you’re waiting on now, that’s where the below query comes in.
First it returns the overall waits on the instance since SQL Server startup (or since the last time the wait data was manually cleared). Then it takes a 30 second sample of what your system is waiting on right now. This query will take 30 seconds to run, but don’t worry – it’s not blocking anyone.
- /*
- 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
How to Interpret the Output
Here’s our favorite links for performance tuning SQL Server with wait stats:
INTRODUCTIONS TO WAIT STATS
EXPLANATIONS OF EACH WAIT TYPE
TOOLS TO ANALYZE WAITS AND QUEUES
- sp_WhoIsActive from Adam Machanic – this awesome free replacement for sp_who shows you what’s REALLY happening inside your database server including the queries, wait statistics, and locking/blocking issues. Call it with @get_plans = 1 to see execution plans, too.
- SQLNexus – when you want to analyze performance over time, use this free tool from Microsoft to generate pretty graphs of your SQL Server’s activity. Not for the faint of heart, but we discuss it in detail in chapter 13 of our book, Professional SQL Server 2008 Internals and Troubleshooting.
Next Steps to Learn More
No hay comentarios.:
Publicar un comentario