Производительность SQL Server: доминирующий тип ожидания PREEMPTIVE_OS_DELETESECURITYCONTEXT

8

Вчера мне позвонил клиент, который жаловался на высокую загрузку ЦП на своем SQL Server. Мы используем SQL Server 2012 64 бит SE. Сервер работает под управлением Windows Server 2008 R2 Standard, Intel Xeon 2,20 ГГц (4 ядра), 16 ГБ ОЗУ.

Убедившись , что преступник был на самом деле SQL Server, я решил посмотреть на топ ждет экземпляра с помощью запроса DMV здесь . Два верхних ожидания были: (1) PREEMPTIVE_OS_DELETESECURITYCONTEXTи (2) SOS_SCHEDULER_YIELD.

РЕДАКТИРОВАТЬ : Вот результат "запроса на ожидание" (хотя кто-то перезапустил сервер этим утром против моих пожеланий):

введите описание изображения здесь

Мы делаем много интенсивных расчетов / преобразований, поэтому я могу понять SOS_SCHEDULER_YIELD. Однако мне очень интересно узнать PREEMPTIVE_OS_DELETESECURITYCONTEXTтип ожидания и почему он может быть самым высоким.

Лучшее описание / обсуждение, которое я могу найти по этому типу ожидания, можно найти здесь . Это упоминает:

Типы ожидания PREEMPTIVE_OS_ - это вызовы, которые покидают ядро ​​базы данных, как правило, для Win32 API, и выполняют код вне SQL Server для выполнения различных задач. В этом случае он удаляет контекст безопасности, ранее использовавшийся для удаленного доступа к ресурсам. Связанный API фактически называется DeleteSecurityContext ()

Насколько мне известно, у нас нет внешних ресурсов, таких как связанные серверы или файловые таблицы. И мы не делаем олицетворения и т. Д. Может ли резервное копирование вызвать скачок или, возможно, неисправен контроллер домена?

Что, черт возьми, может заставить это быть доминирующим типом ожидания? Как я могу отслеживать этот тип ожидания дальше?

Изменить 2: я проверил содержимое журнала безопасности Windows. Я вижу несколько записей, которые могут представлять интерес, но я не уверен, что это нормально:

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLServerOLAPService
    Account Name:       MSSQLServerOLAPService
    Account Domain:     NT Service
    Logon ID:       0x3143c

Privileges:     SeImpersonatePrivilege

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLSERVER
    Account Name:       MSSQLSERVER
    Account Domain:     NT Service
    Logon ID:       0x2f872

Privileges:     SeAssignPrimaryTokenPrivilege
            SeImpersonatePrivilege

Редактировать 3 : @Jon Seigel, как вы и просили, вот результаты вашего запроса. Немного отличается от Пола:

введите описание изображения здесь

Изменить 4: Я признаю, я впервые расширенный пользователь событий. Я добавил этот тип ожидания к событию wait_info_external и увидел сотни записей. Здесь нет текста или дескриптора плана, только стек вызовов. Как я могу дополнительно отследить источник?

введите описание изображения здесь

Джон Рассел
источник
Джон, не могли бы вы, пожалуйста, запустить sp_whoisactive на некоторое время (возможно, минуту) и посмотреть, что всплывает? Это может помочь направить вас / нас к решению. sqlblog.com/files/default.aspx
Марк Уилкинсон
Здравствуйте, Джон, в своем вопросе вы упоминаете, что идентифицировали SQL Server как виновника. Не могли бы вы описать шаги, которые вы предприняли, чтобы прийти к такому выводу?
Крейг Эфрейн

Ответы:

3

Я знаю, что этот вопрос, основанный на заголовке, в основном касается типа ожидания PREEMPTIVE_OS_DELETESECURITYCONTEXT, но я считаю, что это неверное направление истинной проблемы, которой является « клиент, который жаловался на высокую загрузку ЦП на своем SQL Server ».

Причина, по которой я считаю, что сосредоточение внимания на этом конкретном типе ожидания - это погоня за диким гусем, заключается в том, что она повышается для каждого соединения. Я выполняю следующий запрос на моем ноутбуке (то есть я единственный пользователь):

SELECT * 
FROM sys.dm_os_wait_stats
WHERE wait_type = N'PREEMPTIVE_OS_DELETESECURITYCONTEXT'

И затем я делаю любое из следующего и повторно запускаю этот запрос:

  • открыть новую вкладку запроса
  • закрыть вкладку нового запроса
  • запустите следующее из командной строки DOS: SQLCMD -E -Q "select 1"

Теперь мы знаем, что ЦП высок, поэтому мы должны посмотреть на то, что работает, чтобы увидеть, какие сессии имеют высокий ЦП:

SELECT req.session_id AS [SPID],
       req.blocking_session_id AS [BlockedBy],
       req.logical_reads AS [LogReads],
       DB_NAME(req.database_id) AS [DatabaseName],
       SUBSTRING(txt.[text],
                 (req.statement_start_offset / 2) + 1,
                 CASE
                     WHEN req.statement_end_offset > 0
                        THEN (req.statement_end_offset - req.statement_start_offset) / 2
                     ELSE LEN(txt.[text])
                 END
                ) AS [CurrentStatement],
       txt.[text] AS [CurrentBatch],
       CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
       OBJECT_NAME(qplan.objectid, qplan.[dbid]) AS [ObjectName],
       sess.[program_name],
       sess.[host_name],
       sess.nt_user_name,
       sess.total_scheduled_time,
       sess.memory_usage,
       req.*
FROM sys.dm_exec_requests req
INNER JOIN sys.dm_exec_sessions sess
        ON sess.session_id = req.session_id
CROSS APPLY sys.dm_exec_sql_text(req.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(req.plan_handle,
                                        req.statement_start_offset,
                                        req.statement_end_offset) qplan
WHERE req.session_id <> @@SPID
ORDER BY req.logical_reads DESC, req.cpu_time DESC
--ORDER BY req.cpu_time DESC, req.logical_reads DESC

Я обычно запускаю приведенный выше запрос как есть, но вы также можете переключиться, какое предложение ORDER BY закомментировано, чтобы увидеть, дает ли это более интересные / полезные результаты.

В качестве альтернативы вы можете запустить следующее, основанное на dm_exec_query_stats, чтобы найти самые дорогие запросы. Первый запрос ниже покажет вам отдельные запросы (даже если они имеют несколько планов) и упорядочен по среднему времени ЦП, но вы можете легко изменить его на Среднее логическое чтение. Как только вы найдете запрос, который выглядит так, как будто он занимает много ресурсов, скопируйте "sql_handle" и "Statement_start_offset" в условие WHERE второго запроса ниже, чтобы увидеть отдельные планы (может быть больше 1). Прокрутите в крайнее правое положение и, предположив, что существует план XML, он будет отображаться в виде ссылки (в режиме сетки), которая приведет вас к средству просмотра плана, если вы щелкните по нему.

Запрос № 1: Получить информацию о запросе

;WITH cte AS
(
   SELECT qstat.[sql_handle],
          qstat.statement_start_offset,
          qstat.statement_end_offset,
          COUNT(*) AS [NumberOfPlans],
          SUM(qstat.execution_count) AS [TotalExecutions],

          SUM(qstat.total_worker_time) AS [TotalCPU],
          (SUM(qstat.total_worker_time * 1.0) / SUM(qstat.execution_count)) AS [AvgCPUtime],
          MAX(qstat.max_worker_time) AS [MaxCPU],

          SUM(qstat.total_logical_reads) AS [TotalLogicalReads],
   (SUM(qstat.total_logical_reads * 1.0) / SUM(qstat.execution_count)) AS [AvgLogicalReads],
          MAX(qstat.max_logical_reads) AS [MaxLogicalReads],

          SUM(qstat.total_rows) AS [TotalRows],
          (SUM(qstat.total_rows * 1.0) / SUM(qstat.execution_count)) AS [AvgRows],
          MAX(qstat.max_rows) AS [MaxRows]
   FROM sys.dm_exec_query_stats  qstat
   GROUP BY qstat.[sql_handle], qstat.statement_start_offset, qstat.statement_end_offset
)
SELECT  cte.*,
        DB_NAME(txt.[dbid]) AS [DatabaseName],
        SUBSTRING(txt.[text],
                  (cte.statement_start_offset / 2) + 1,
                  CASE
                      WHEN cte.statement_end_offset > 0
                          THEN (cte.statement_end_offset - cte.statement_start_offset) / 2
                      ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM cte
CROSS APPLY sys.dm_exec_sql_text(cte.[sql_handle]) txt
ORDER BY cte.AvgCPUtime DESC

Запрос № 2: Получить информацию о плане

SELECT  *,
        DB_NAME(qplan.[dbid]) AS [DatabaseName],
        CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
        SUBSTRING(txt.[text],
                  (qstat.statement_start_offset / 2) + 1,
                  CASE
                        WHEN qstat.statement_end_offset > 0
                        THEN (qstat.statement_end_offset - qstat.statement_start_offset) / 2
                        ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM sys.dm_exec_query_stats  qstat
CROSS APPLY sys.dm_exec_sql_text(qstat.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(qstat.plan_handle,
                                        qstat.statement_start_offset,
                                        qstat.statement_end_offset) qplan
-- paste info from Query #1 below
WHERE qstat.[sql_handle] = 0x020000001C70C614D261C85875D4EF3C90BD18D02D62453800....
AND qstat.statement_start_offset = 164
-- paste info from Query #1 above
ORDER BY qstat.total_worker_time DESC
Соломон Руцкий
источник
Я подумал, что причиной ожидания процессора может быть самый высокий тип ожидания из сценария Пола, а именно PREEMPTIVE_OS_DELETESECURITYCONTEXT. Можно ли считать это безопасным типом ожидания в нашем случае? В нашем приложении у нас есть несколько служб Windows, которые постоянно отправляют команды (исполняемые хранимые процедуры) на SQL Server. Я не могу различить слишком много шаблонов из sys.dm_exec_sessions - сессии не остаются открытыми слишком долго, и их много. sys.dm_exec_query_stats предоставляет полезную информацию о самых дорогих хранимых процессах, а также общую стоимость процессора. Это может быть хорошим местом для начала.
Джон Рассел
Я просто хотел убедиться, что я ничего не пропустил с PREEMPTIVE_OS_DELETESECURITYCONTEXT. Я не знал, может ли это быть связано с неисправным контроллером домена или поиском AD?
Джон Рассел
@JohnRussell: я думаю, что самый высокий тип ожидания, как правило, является хорошим началом xp_dirtree), следовательно, большой объем не является истинным показателем. И даже если задержка в сети приводит к задержкам, действительно ли это повысит процессор или только увеличит блокировку? И хороший момент, используйте query_stats. Я обновлю свой запрос позже с этим.
Соломон Руцкий,
1
@JohnRussell: что-нибудь изменилось в последнее время относительно ваших "служб Windows, которые постоянно отправляют команды"? Они правильно закрывают соединения? Правильно ли они очищают соединение, если при подключении произошла ошибка? Кроме того, вы недавно перестроили индексы или хотя бы обновили статистику по всем таблицам? Невыполнение этого может привести к увеличению процессора.
Соломон Руцкий,
Спасибо за понимание! По мере приближения к sys.dm_exec_query_stats и фрагментации индекса по нескольким ключевым таблицам я начинаю чувствовать себя более уверенно относительно причины. PREEMPTIVE_OS_DELETESECURITYCONTEXT просто отбросил меня.
Джон Рассел
1

SecurityContext используется сервером sql в нескольких местах. Одним из примеров, который вы назвали, являются связанные серверы и таблицы файлов. Может быть, вы используете cmdexec? Задания агента SQL Server с учетными записями прокси? Вызываете веб-сервис? Удаленные ресурсы могут быть много забавных вещей.

События олицетворения могут быть зарегистрированы в событии безопасности Windows. Может быть, вы нашли подсказку там. Кроме того, вы можете проверить записывающее устройство черного ящика или расширенные события.

Вы проверили, являются ли эти Типы ожидания новыми (и в связи с высокой ЦП) или просто нормальными для вашего сервера?

Йенс В.
источник
У нас нет ни заданий агента SQL Server, ни WebServices. Я очистил статистику ожидания и перезапустил исходный запрос выше, и снова появилась похожая статистика. Мне потребовалось немного времени, чтобы понять, как перенастроить сеанс расширенного события system_health, чтобы включить wait_info_external для waittype = 'PREEMPTIVE_OS_DELETESECURITYCONTEXT', но, наконец, я его добавил, я вижу сотни этих событий за несколько секунд, которые я наблюдал за живыми данными , Я смотрю, как лучше расшифровать источник. Любой совет, как отследить это?
Джон Рассел