lunes, 27 de junio de 2016

Triage Wait Stats in SQL Server



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.
  1. /*
  2. SQL Server Wait Information from sys.dm_os_wait_stats
  3. Copyright (C) 2014, Brent Ozar Unlimited.
  4. See http://BrentOzar.com/go/eula for the End User Licensing Agreement.
  5. */
  6. /*********************************
  7. Let's build a list of waits we can safely ignore.
  8. *********************************/
  9. IF OBJECT_ID('tempdb..#ignorable_waits') IS NOT NULL
  10. DROP TABLE #ignorable_waits;
  11. GO
  12. create table #ignorable_waits (wait_type nvarchar(256) PRIMARY KEY);
  13. GO
  14. /* We aren't using row constructors to be SQL 2005 compatible */
  15. set nocount on;
  16. insert #ignorable_waits (wait_type) VALUES ('REQUEST_FOR_DEADLOCK_SEARCH');
  17. insert #ignorable_waits (wait_type) VALUES ('SQLTRACE_INCREMENTAL_FLUSH_SLEEP');
  18. insert #ignorable_waits (wait_type) VALUES ('SQLTRACE_BUFFER_FLUSH');
  19. insert #ignorable_waits (wait_type) VALUES ('LAZYWRITER_SLEEP');
  20. insert #ignorable_waits (wait_type) VALUES ('XE_TIMER_EVENT');
  21. insert #ignorable_waits (wait_type) VALUES ('XE_DISPATCHER_WAIT');
  22. insert #ignorable_waits (wait_type) VALUES ('FT_IFTS_SCHEDULER_IDLE_WAIT');
  23. insert #ignorable_waits (wait_type) VALUES ('LOGMGR_QUEUE');
  24. insert #ignorable_waits (wait_type) VALUES ('CHECKPOINT_QUEUE');
  25. insert #ignorable_waits (wait_type) VALUES ('BROKER_TO_FLUSH');
  26. insert #ignorable_waits (wait_type) VALUES ('BROKER_TASK_STOP');
  27. insert #ignorable_waits (wait_type) VALUES ('BROKER_EVENTHANDLER');
  28. insert #ignorable_waits (wait_type) VALUES ('SLEEP_TASK');
  29. insert #ignorable_waits (wait_type) VALUES ('WAITFOR');
  30. insert #ignorable_waits (wait_type) VALUES ('DBMIRROR_DBM_MUTEX')
  31. insert #ignorable_waits (wait_type) VALUES ('DBMIRROR_EVENTS_QUEUE')
  32. insert #ignorable_waits (wait_type) VALUES ('DBMIRRORING_CMD');
  33. insert #ignorable_waits (wait_type) VALUES ('DISPATCHER_QUEUE_SEMAPHORE');
  34. insert #ignorable_waits (wait_type) VALUES ('BROKER_RECEIVE_WAITFOR');
  35. insert #ignorable_waits (wait_type) VALUES ('CLR_AUTO_EVENT');
  36. insert #ignorable_waits (wait_type) VALUES ('DIRTY_PAGE_POLL');
  37. insert #ignorable_waits (wait_type) VALUES ('HADR_FILESTREAM_IOMGR_IOCOMPLETION');
  38. insert #ignorable_waits (wait_type) VALUES ('ONDEMAND_TASK_QUEUE');
  39. insert #ignorable_waits (wait_type) VALUES ('FT_IFTSHC_MUTEX');
  40. insert #ignorable_waits (wait_type) VALUES ('CLR_MANUAL_EVENT');
  41. insert #ignorable_waits (wait_type) VALUES ('SP_SERVER_DIAGNOSTICS_SLEEP');
  42. insert #ignorable_waits (wait_type) VALUES ('QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP');
  43. insert #ignorable_waits (wait_type) VALUES ('QDS_PERSIST_TASK_MAIN_LOOP_SLEEP');
  44. GO
  45. /* Want to manually exclude an event and recalculate?*/
  46. /* insert #ignorable_waits (wait_type) VALUES (''); */
  47. /*********************************
  48. What are the highest overall waits since startup?
  49. *********************************/
  50. SELECT TOP 25
  51. os.wait_type,
  52. SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
  53. CAST(
  54. 100.* SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type)
  55. / (1. * SUM(os.wait_time_ms) OVER () )
  56. AS NUMERIC(12,1)) as pct_wait_time,
  57. SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks,
  58. CASE WHEN SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) > 0
  59. THEN
  60. CAST(
  61. SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type)
  62. / (1. * SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type))
  63. AS NUMERIC(12,1))
  64. ELSE 0 END AS avg_wait_time_ms,
  65. CURRENT_TIMESTAMP as sample_time
  66. FROM sys.dm_os_wait_stats os
  67. LEFT JOIN #ignorable_waits iw on
  68. os.wait_type=iw.wait_type
  69. WHERE
  70. iw.wait_type is null
  71. ORDER BY sum_wait_time_ms DESC;
  72. GO
  73. /*********************************
  74. What are the higest waits *right now*?
  75. *********************************/
  76. /* Note: this is dependent on the #ignorable_waits table created earlier. */
  77. if OBJECT_ID('tempdb..#wait_batches') is not null
  78. drop table #wait_batches;
  79. if OBJECT_ID('tempdb..#wait_data') is not null
  80. drop table #wait_data;
  81. GO
  82. CREATE TABLE #wait_batches (
  83. batch_id int identity primary key,
  84. sample_time datetime not null
  85. );
  86. CREATE TABLE #wait_data
  87. ( batch_id INT NOT NULL ,
  88. wait_type NVARCHAR(256) NOT NULL ,
  89. wait_time_ms BIGINT NOT NULL ,
  90. waiting_tasks BIGINT NOT NULL
  91. );
  92. CREATE CLUSTERED INDEX cx_wait_data on #wait_data(batch_id);
  93. GO
  94. /*
  95. This temporary procedure records wait data to a temp table.
  96. */
  97. if OBJECT_ID('tempdb..#get_wait_data') IS NOT NULL
  98. DROP procedure #get_wait_data;
  99. GO
  100. CREATE PROCEDURE #get_wait_data
  101. @intervals tinyint = 2,
  102. @delay char(12)='00:00:30.000' /* 30 seconds*/
  103. AS
  104. DECLARE @batch_id int,
  105. @current_interval tinyint,
  106. @msg nvarchar(max);
  107. SET NOCOUNT ON;
  108. SET @current_interval=1;
  109. WHILE @current_interval <= @intervals
  110. BEGIN
  111. INSERT #wait_batches(sample_time)
  112. SELECT CURRENT_TIMESTAMP;
  113. SELECT @batch_id=SCOPE_IDENTITY();
  114. INSERT #wait_data (batch_id, wait_type, wait_time_ms, waiting_tasks)
  115. SELECT
  116. @batch_id,
  117. os.wait_type,
  118. SUM(os.wait_time_ms) OVER (PARTITION BY os.wait_type) as sum_wait_time_ms,
  119. SUM(os.waiting_tasks_count) OVER (PARTITION BY os.wait_type) AS sum_waiting_tasks
  120. FROM sys.dm_os_wait_stats os
  121. LEFT JOIN #ignorable_waits iw on
  122. os.wait_type=iw.wait_type
  123. WHERE
  124. iw.wait_type is null
  125. ORDER BY sum_wait_time_ms DESC;
  126. set @msg= CONVERT(char(23),CURRENT_TIMESTAMP,121)+ N': Completed sample '
  127. + cast(@current_interval as nvarchar(4))
  128. + N' of ' + cast(@intervals as nvarchar(4)) +
  129. '.'
  130. RAISERROR (@msg,0,1) WITH NOWAIT;
  131. SET @current_interval=@current_interval+1;
  132. if @current_interval <= @intervals
  133. WAITFOR DELAY @delay;
  134. END
  135. GO
  136. /*
  137. Let's take two samples 30 seconds apart
  138. */
  139. exec #get_wait_data @intervals=2, @delay='00:00:30.000';
  140. GO
  141. /*
  142. What were we waiting on?
  143. This query compares the most recent two samples.
  144. */
  145. with max_batch as (
  146. select top 1 batch_id, sample_time
  147. from #wait_batches
  148. order by batch_id desc
  149. )
  150. SELECT
  151. b.sample_time as [Second Sample Time],
  152. datediff(ss,wb1.sample_time, b.sample_time) as [Sample Duration in Seconds],
  153. wd1.wait_type,
  154. cast((wd2.wait_time_ms-wd1.wait_time_ms)/1000. as numeric(12,1)) as [Wait Time (Seconds)],
  155. (wd2.waiting_tasks-wd1.waiting_tasks) AS [Number of Waits],
  156. CASE WHEN (wd2.waiting_tasks-wd1.waiting_tasks) > 0
  157. THEN
  158. cast((wd2.wait_time_ms-wd1.wait_time_ms)/
  159. (1.0*(wd2.waiting_tasks-wd1.waiting_tasks)) as numeric(12,1))
  160. ELSE 0 END AS [Avg ms Per Wait]
  161. FROM max_batch b
  162. JOIN #wait_data wd2 on
  163. wd2.batch_id=b.batch_id
  164. JOIN #wait_data wd1 on
  165. wd1.wait_type=wd2.wait_type AND
  166. wd2.batch_id - 1 = wd1.batch_id
  167. join #wait_batches wb1 on
  168. wd1.batch_id=wb1.batch_id
  169. WHERE (wd2.waiting_tasks-wd1.waiting_tasks) > 0
  170. ORDER BY [Wait Time (Seconds)] DESC;
  171. 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

This query is part of our free SQL Server First Responder Kit. It’s checklists and tools to help you fix performance problems fast.
Check out our free training videos – every Tuesday, we offer free webcasts, and the past recorded episodes are available in our Video Archives.
And for even more advanced details, read about our upcoming in-person training classes.

No hay comentarios.:

Publicar un comentario