- PVSM.RU - https://www.pvsm.ru -
Сколько раз вы испытывали проблемы с производительностью SQL Server'а и решали, куда именно смотреть?
Одна из самых редко используемых методологий устранения проблем с производительностью SQL Server'а называется «Ожидания и очереди» (также известная как «статистика ожиданий»). Основная предпосылка методологии состоит в том, что SQL Server постоянно отслеживает, какие потоки выполнения должны ждать. Вы можете запросить у SQL Server'а эту информацию для того чтобы сократить перечень возможных причин проблем с производительностью. «Ожидания» — это то, что отслеживает SQL Server. «Очереди» — это ресурсы, доступ к которым ожидают потоки. Система обычно фиксирует огромное количество ожиданий, и все они означают ожидание доступа к различным ресурсам. Для примера, ожидание PAGEIOLATCH_EX означает, что поток ожидает чтения страницы данных с диска в буферный пул. Ожидание LCK_M_X означает, что поток ожидает возможности наложить эксклюзивную блокировку на что-то.
Отличная новость состоит в том, что SQL Server знает, в чем именно заключаются проблемы с производительностью, и все что вам нужно — это спросить у него… и потом правильно интерпретировать то, что он скажет, что может быть немного сложнее.
Следующая информация — для людей, которые пытаются отследить каждое ожидание и понять, что его вызывает. Ожидания возникают всегда. Это тот способ, которым SQL Server планирует работу.
Поток использует процессор и имеет статус «выполняется» (RUNNING) до тех пор, пока не встречается с необходимостью дождаться доступа к ресурсу. В этом случае он помещается в неупорядоченный список потоков в состоянии «приостановлен» (SUSPENDED). В то же время, следующий поток в очереди потоков, ожидающих доступ к процессору, организованной по принципу FIFO (первым поступил — первым выбыл), и имеющих статус «готов к выполнению» (RUNNABLE) получает доступ к процессору и становится «выполняющимся». Если поток в состоянии «приостановлен» получает уведомление о том, что его ресурс доступен, он становится «готовым к выполнению» и помещается в конец очереди готовых к выполнению потоков. Поток продолжает свое циклическое движение по цепочке «выполняется» — «приостановлен» — «готов к выполнению» до тех пор, пока задание не выполнено. В можете видеть процессы и их состояния, используя динамическое административное представление (Dynamic Management View, DMV) sys.dm_exec_requests.
SQL Server отслеживает время, которое проходит между выходом потока из состояния «выполняется» и его возвращением в это состояние, называя это «временем ожидания» (wait time) и время, потраченное в состоянии «готов к выполнению», называя это «временем ожидания сигнала» (signal wait time), т.е. сколько времени требуется потоку после получения сигнала о доступности ресурсов для того, чтобы получить доступ к процессору. Мы должны понять, сколько времени тратит поток в состоянии «приостановлен», называемом «временем ожидания ресурсов» (resource wait time), вычитая время ожидания сигнала из общего времени ожидания.
Отличный источник информации, который я рекомендую вам прочитать по этому поводу — документ по SQL Server 2005 «Регулировка производительности с использованием ожиданий и очередей» [1] (англ.). Онлайновый справочник (Books Online) также имеет раздел, посвященный DMV sys.dm_os_wait_stats [2], в котором дается информация о некоторых новых типах ожиданий.
Вы можете запросить SQL Server о накопленной статистике ожидания, используя DMV sys.dm_os_wait_stats. Многие люди предпочитают обернуть вызов DMV в некий сводный код. Я использую код, основанный на запросе, который я получил от своего MVP-приятеля Глена Берри (его блог [3], твиттер [4]), и затем слегка модифицировал его. Ниже смотрите версию, используемую для того, чтобы принять во внимание результаты, которые мы будем обсуждать ниже:
WITH [Waits] AS
(SELECT
[wait_type],
[wait_time_ms] / 1000.0 AS [WaitS],
([wait_time_ms] - [signal_wait_time_ms]) / 1000.0 AS [ResourceS],
[signal_wait_time_ms] / 1000.0 AS [SignalS],
[waiting_tasks_count] AS [WaitCount],
100.0 * [wait_time_ms] / SUM ([wait_time_ms]) OVER() AS [Percentage],
ROW_NUMBER() OVER(ORDER BY [wait_time_ms] DESC) AS [RowNum]
FROM sys.dm_os_wait_stats
WHERE [wait_type] NOT IN (
N'BROKER_EVENTHANDLER', N'BROKER_RECEIVE_WAITFOR',
N'BROKER_TASK_STOP', N'BROKER_TO_FLUSH',
N'BROKER_TRANSMITTER', N'CHECKPOINT_QUEUE',
N'CHKPT', N'CLR_AUTO_EVENT',
N'CLR_MANUAL_EVENT', N'CLR_SEMAPHORE',
N'DBMIRROR_DBM_EVENT', N'DBMIRROR_EVENTS_QUEUE',
N'DBMIRROR_WORKER_QUEUE', N'DBMIRRORING_CMD',
N'DIRTY_PAGE_POLL', N'DISPATCHER_QUEUE_SEMAPHORE',
N'EXECSYNC', N'FSAGENT',
N'FT_IFTS_SCHEDULER_IDLE_WAIT', N'FT_IFTSHC_MUTEX',
N'HADR_CLUSAPI_CALL', N'HADR_FILESTREAM_IOMGR_IOCOMPLETION',
N'HADR_LOGCAPTURE_WAIT', N'HADR_NOTIFICATION_DEQUEUE',
N'HADR_TIMER_TASK', N'HADR_WORK_QUEUE',
N'KSOURCE_WAKEUP', N'LAZYWRITER_SLEEP',
N'LOGMGR_QUEUE', N'ONDEMAND_TASK_QUEUE',
N'PWAIT_ALL_COMPONENTS_INITIALIZED',
N'QDS_PERSIST_TASK_MAIN_LOOP_SLEEP',
N'QDS_CLEANUP_STALE_QUERIES_TASK_MAIN_LOOP_SLEEP',
N'REQUEST_FOR_DEADLOCK_SEARCH', N'RESOURCE_QUEUE',
N'SERVER_IDLE_CHECK', N'SLEEP_BPOOL_FLUSH',
N'SLEEP_DBSTARTUP', N'SLEEP_DCOMSTARTUP',
N'SLEEP_MASTERDBREADY', N'SLEEP_MASTERMDREADY',
N'SLEEP_MASTERUPGRADED', N'SLEEP_MSDBSTARTUP',
N'SLEEP_SYSTEMTASK', N'SLEEP_TASK',
N'SLEEP_TEMPDBSTARTUP', N'SNI_HTTP_ACCEPT',
N'SP_SERVER_DIAGNOSTICS_SLEEP', N'SQLTRACE_BUFFER_FLUSH',
N'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
N'SQLTRACE_WAIT_ENTRIES', N'WAIT_FOR_RESULTS',
N'WAITFOR', N'WAITFOR_TASKSHUTDOWN',
N'WAIT_XTP_HOST_WAIT', N'WAIT_XTP_OFFLINE_CKPT_NEW_LOG',
N'WAIT_XTP_CKPT_CLOSE', N'XE_DISPATCHER_JOIN',
N'XE_DISPATCHER_WAIT', N'XE_TIMER_EVENT')
)
SELECT
[W1].[wait_type] AS [WaitType],
CAST ([W1].[WaitS] AS DECIMAL (16, 2)) AS [Wait_S],
CAST ([W1].[ResourceS] AS DECIMAL (16, 2)) AS [Resource_S],
CAST ([W1].[SignalS] AS DECIMAL (16, 2)) AS [Signal_S],
[W1].[WaitCount] AS [WaitCount],
CAST ([W1].[Percentage] AS DECIMAL (5, 2)) AS [Percentage],
CAST (([W1].[WaitS] / [W1].[WaitCount]) AS DECIMAL (16, 4)) AS [AvgWait_S],
CAST (([W1].[ResourceS] / [W1].[WaitCount]) AS DECIMAL (16, 4)) AS [AvgRes_S],
CAST (([W1].[SignalS] / [W1].[WaitCount]) AS DECIMAL (16, 4)) AS [AvgSig_S]
FROM [Waits] AS [W1]
INNER JOIN [Waits] AS [W2]
ON [W2].[RowNum] <= [W1].[RowNum]
GROUP BY [W1].[RowNum], [W1].[wait_type], [W1].[WaitS],
[W1].[ResourceS], [W1].[SignalS], [W1].[WaitCount], [W1].[Percentage]
HAVING SUM ([W2].[Percentage]) - [W1].[Percentage] < 95; -- percentage threshold
GO
Результат запроса покажет ожидания, сгруппированные по процентам от всех ожиданий в системе, в порядке убывания. Ожидания, на которые (потенциально) стоит обратить внимание, находятся в верхней части списка и представляют собой большую часть ожиданий, на которые тратит время SQL Server. Вы видите большой перечень ожиданий, которые убраны из рассмотрения — как я и говорил ранее, ожидания возникают всегда и те, что перечислены выше, мы можем, как правило, игнорировать.
Вы также можете сбросить накопленную сервером статистику, используя этот код:
DBCC SQLPERF (N'sys.dm_os_wait_stats', CLEAR);
GO
И, конечно же, вы можете придти к тому, чтобы сохранять результаты каждые несколько часов или каждый день и делать некоторый временной анализ для того, чтобы выяснить направление изменений, или автоматически отслеживать проблемы в случае, если они начинают появляться.
Вы можете также использовать панель мониторинга производительности (Performance Dashboard) для того, чтобы отобразить результаты графически в SQL Server 2005 и Сборщик данных (Data Collector) в SQL Server 2008. В в SQL Server 2000 вы можете использовать DBCC SQLPERF (N’waitstats’).
После того, как вы получите результаты, вы начнете искать, как их интерпретировать и куда смотреть. Документ, на который я ссылался ранее, имеет огромное количество информации по большинству типов ожиданий (за исключением добавленных в SQL Server 2008).
Теперь мне хотелось бы предоставить результаты исследования, которое я опубликовал некоторое время назад. Я просил людей запустить код, представленный выше и сообщить мне о результатах. Я получил коллосальное количество результатов — с 1823 серверов – спасибо!
Ниже — графическое представление результатов:

Я совсем не удивлен верхним 4 результатам, поскольку я видел их снова и снова на системах моих клиентов.
В продолжение своей статьи я собираюсь перечислить самые популярные типы ожиданий, предоставленные респондентами исследования, в порядке убывания, и прокомментировать в нескольких словах, что именно они могут значить в случае, если они являются основными для вашей системы. Формат списка показывает количество систем из опрошенных, в которых указанный тип ожидания является основным.
-- Плохо: Ср.задержка одной операции > 20 мсек
USE master
GO
SELECT cast(db_name(a.database_id) AS VARCHAR) AS Database_Name
, b.physical_name
--, a.io_stall
, a.size_on_disk_bytes
, a.io_stall_read_ms / a.num_of_reads 'Ср.задержка одной операции чтения'
, a.io_stall_write_ms / a.num_of_writes 'Ср.задержка одной операции записи'
--, *
FROM
sys.dm_io_virtual_file_stats(NULL, NULL) a
INNER JOIN sys.master_files b
ON a.database_id = b.database_id AND a.file_id = b.file_id
where num_of_writes > 0 and num_of_reads > 0
ORDER BY
Database_Name
, a.io_stall DESC
Надеюсь, это было интересно! Дайте мне знать, если вы заинтересованы в чем то конкретно или что вы прочитали эту статью и получили удовольствие от этого!
Автор: minamoto
Источник [11]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/news/57351
Ссылки в тексте:
[1] «Регулировка производительности с использованием ожиданий и очередей»: http://download.microsoft.com/download/4/7/a/47a548b9-249e-484c-abd7-29f31282b04d/Performance_Tuning_Waits_Queues.doc
[2] sys.dm_os_wait_stats: http://technet.microsoft.com/ru-ru/library/ms179984.aspx
[3] блог: http://www.sqlskills.com/blogs/glenn
[4] твиттер: https://twitter.com/GlennAlanBerry
[5] Здесь: http://www.sqlperformance.com/2012/12/io-subsystem/trimming-t-log-fat
[6] здесь: http://www.sqlperformance.com/2013/01/io-subsystem/trimming-more-transaction-log-fat
[7] возникающие в базе tempdb при определенных типах нагрузок: http://www.sqlskills.com/blogs/paul/a-sql-server-dba-myth-a-day-1230-tempdb-should-always-have-one-data-file-per-processor-core/
[8] блог: http://www.sqlservercentral.com/blogs/robert_davis/
[9] твиттер: https://twitter.com/SQLSoldier
[10] статью, показывающую, как их решать: http://www.sqlservercentral.com/blogs/robert_davis/2010/03/05/Breaking-Down-TempDB-Contention/
[11] Источник: http://habrahabr.ru/post/216309/
Нажмите здесь для печати.