Прерывистый RESOURCE_SEMAPHORE_QUERY_COMPILE Статистика ожидания

8

Я пытаюсь устранить некоторые прерывистые пики ЦП, которые мы наблюдаем на одном из наших производственных SQL-серверов. Мы используем SQL Server 2008 R2 Standard Edition с 28 ГБ оперативной памяти и 4 ядрами процессора. Когда это происходит, мы замечаем большое количество ожиданий RESOURCE_SEMAPHORE_QUERY_COMPILER, которое длится около минуты или двух, а затем останавливается, после чего загрузка ЦП возвращается к нормальному.

Изучив это, я понимаю, что это обычно вызывается компиляцией большого количества неиспользуемых планов выполнения, над которыми мы сейчас работаем над изменениями в нашем приложении.

Может ли это поведение также быть вызвано удалением кэша плана из-за нехватки памяти? Если так, то как бы я проверил это? Я пытаюсь выяснить, есть ли какие-либо краткосрочные меры, которые мы можем предпринять, такие как обновление ОЗУ сервера, до тех пор, пока мы не развернем исправления нашего приложения. Единственный другой краткосрочный вариант, о котором я могу подумать, - это перенести некоторые из наших самых загруженных баз данных на другой сервер.

даН.м.
источник

Ответы:

6

Я полагаю, вы увидите этот симптом, если у вас будет МНОГО больших планов запросов, которые борются за память для компиляции (это очень мало связано с выполнением самого запроса). Я подозреваю, что для этого вы используете ORM или какое-то приложение, которое генерирует множество уникальных, но относительно сложных запросов. SQL Server может испытывать нехватку памяти из-за таких вещей, как операции с большими запросами, но, если подумать, более вероятно, что ваша система сконфигурирована с гораздо меньшим объемом памяти, чем нужно (либо памяти никогда не будет достаточно для удовлетворения всех запросов, которые вы пытаетесь скомпилировать, или на коробке есть другие процессы, которые крадут память у SQL Server).

Вы можете взглянуть на то, что SQL Server настроен с помощью:

EXEC sp_configure 'max server memory';    -- max configured in MB

SELECT counter_name, cntr_value
  FROM sys.dm_os_performance_counters
  WHERE counter_name IN
  (
    'Total Server Memory (KB)',    -- max currently granted
    'Target Server Memory (KB)'    -- how much SQL Server wished it had
  );

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

SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;

;WITH XMLNAMESPACES (DEFAULT 'http://schemas.microsoft.com/sqlserver/2004/07/showplan')
SELECT TOP (10) CompileTime_ms, CompileCPU_ms, CompileMemory_KB,
  qs.execution_count,
  qs.total_elapsed_time/1000.0 AS duration_ms,
  qs.total_worker_time/1000.0 as cputime_ms,
  (qs.total_elapsed_time/qs.execution_count)/1000.0 AS avg_duration_ms,
  (qs.total_worker_time/qs.execution_count)/1000.0 AS avg_cputime_ms,
  qs.max_elapsed_time/1000.0 AS max_duration_ms,
  qs.max_worker_time/1000.0 AS max_cputime_ms,
  SUBSTRING(st.text, (qs.statement_start_offset / 2) + 1,
    (CASE qs.statement_end_offset
      WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset
     END - qs.statement_start_offset) / 2 + 1) AS StmtText,
  query_hash, query_plan_hash
FROM
(
  SELECT 
    c.value('xs:hexBinary(substring((@QueryHash)[1],3))', 'varbinary(max)') AS QueryHash,
    c.value('xs:hexBinary(substring((@QueryPlanHash)[1],3))', 'varbinary(max)') AS QueryPlanHash,
    c.value('(QueryPlan/@CompileTime)[1]', 'int') AS CompileTime_ms,
    c.value('(QueryPlan/@CompileCPU)[1]', 'int') AS CompileCPU_ms,
    c.value('(QueryPlan/@CompileMemory)[1]', 'int') AS CompileMemory_KB,
    qp.query_plan
FROM sys.dm_exec_cached_plans AS cp
CROSS APPLY sys.dm_exec_query_plan(cp.plan_handle) AS qp
CROSS APPLY qp.query_plan.nodes('ShowPlanXML/BatchSequence/Batch/Statements/StmtSimple') AS n(c)
) AS tab
JOIN sys.dm_exec_query_stats AS qs ON tab.QueryHash = qs.query_hash
CROSS APPLY sys.dm_exec_sql_text(qs.sql_handle) AS st
ORDER BY CompileMemory_KB DESC
OPTION (RECOMPILE, MAXDOP 1);

Вы можете увидеть, как кэш плана используется со следующим:

SELECT objtype, cacheobjtype,
    AVG(size_in_bytes*1.0)/1024.0/1024.0,
    MAX(size_in_bytes)/1024.0/1024.0,
    SUM(size_in_bytes)/1024.0/1024.0,
    COUNT(*)
FROM sys.dm_exec_cached_plans
GROUP BY GROUPING SETS ((),(objtype, cacheobjtype))
ORDER BY objtype, cacheobjtype;

Когда вы испытываете большое время ожидания семафора, проверьте, значительно ли отличаются результаты этих запросов от "нормальной" активности:

SELECT resource_semaphore_id, -- 0 = regular, 1 = "small query"
  pool_id,
  available_memory_kb,
  total_memory_kb,
  target_memory_kb
FROM sys.dm_exec_query_resource_semaphores;

SELECT StmtText = SUBSTRING(st.[text], (qs.statement_start_offset / 2) + 1,
        (CASE qs.statement_end_offset
          WHEN -1 THEN DATALENGTH(st.text) ELSE qs.statement_end_offset
         END - qs.statement_start_offset) / 2 + 1),
  r.start_time, r.[status], DB_NAME(r.database_id), r.wait_type, 
  r.last_wait_type, r.total_elapsed_time, r.granted_query_memory,
  m.requested_memory_kb, m.granted_memory_kb, m.required_memory_kb,
  m.used_memory_kb
FROM sys.dm_exec_requests AS r
INNER JOIN sys.dm_exec_query_stats AS qs
ON r.plan_handle = qs.plan_handle
INNER JOIN sys.dm_exec_query_memory_grants AS m
ON r.request_id = m.request_id
AND r.plan_handle = m.plan_handle
CROSS APPLY sys.dm_exec_sql_text(r.plan_handle) AS st;

И вы также можете посмотреть, как распределяется память:

DBCC MEMORYSTATUS;

И здесь есть некоторая хорошая информация о том, почему вы можете увидеть большое количество компиляций / перекомпиляций (что будет способствовать этому ожиданию):

http://technet.microsoft.com/en-us/library/ee343986(v=sql.100).aspx

http://technet.microsoft.com/en-us/library/cc293620.aspx

Вы можете проверить высокие показатели компиляции / перекомпиляции, используя следующие счетчики:

SELECT counter_name, cntr_value
  FROM sys.dm_os_performance_counters
  WHERE counter_name IN 
  (
    'SQL Compilations/sec',
    'SQL Re-Compilations/sec'
  );

И вы можете проверить наличие внутренней памяти, приводящее к выселению - ненулевые счетчики здесь будут указывать, что с кэшем плана происходит что-то нехорошее:

SELECT * FROM sys.dm_os_memory_cache_clock_hands 
  WHERE [type] IN (N'CACHESTORE_SQLCP', N'CACHESTORE_OBJCP');

ПРИМЕЧАНИЕ. Большинство из этих показателей не имеют волшебства: «О, черт возьми, мне нужно паниковать или что-то делать!» порог. Вам нужно провести измерения во время нормальной работы системы и определить, где находятся эти пороговые значения для вашего оборудования, конфигурации и рабочей нагрузки. Когда вы паникуете, вы делаете что-то, когда выполняются два условия:

  1. показатели значительно отличаются от нормальных значений; а также,
  2. на самом деле возникает проблема с производительностью (например, пики вашего процессора) - но только если они на самом деле что-то мешают. Кроме того, что вы видите всплеск процессоров, вы видите какой-нибудь другой симптом? Другими словами, является ли спайк симптомом или спайк вызывает другие симптомы? Заметят ли пользователи системы когда-нибудь? Многие люди всегда идут за своим потребителем с самым высоким ожиданием, просто потому, что он самый высокий. Что-то всегда будет потребителем с наивысшим ожиданием - вы должны знать, что оно достаточно отличается от обычной активности, что указывает на проблему или какое-то существенное изменение.

Optimize for ad hoc workloadsЭто отличная настройка для 99% рабочих нагрузок, но она не будет очень полезна для снижения затрат на компиляцию - она ​​направлена ​​на уменьшение размаха кэша плана за счет предотвращения хранения плана всего одноразового использования до тех пор, пока он не будет выполнен дважды. , Даже когда вы сохраняете только заглушку в кэше планов, вам все равно приходится составлять полный план для выполнения запроса. Возможно, то, что @Kahn хотел порекомендовать, это установить принудительную параметризацию уровня базы данных , что потенциально обеспечит лучшее повторное использование плана (но это действительно зависит от того, насколько уникальны все эти дорогостоящие запросы).

Также есть хорошая информация в этой статье о кэшировании и компиляции планов.

Аарон Бертран
источник
В настоящее время у нас есть Optimize for ad hoc workloadsнабор, хотя, как вы упомянули, он не имеет отношения к этой конкретной проблеме. У нас есть код, который генерирует множество уникальных запросов, некоторые из которых сделаны из инструмента ORM, некоторые из них написаны вручную. Насколько я знаю, пики процессора не происходят достаточно долго, чтобы наши пользователи могли это заметить. Настройка базы данных на принудительную параметризацию звучит для меня опасно.
ДанМ
Один вопрос - когда вы проверяете наличие высоких компиляций, что на самом деле составляет большое число? Я думал, что компиляция / сек была только значимой по сравнению с количеством пакетных запросов / сек.
ДанМ
@DanM, как я уже говорил выше, я не могу узнать, что может быть высоким для вашей среды, потому что я понятия не имею, что нормально для вашей среды. Если это число близко к числу пакетных запросов / сек или превышает его, это может быть индикатором, но, опять же, это зависит. Например, если ваши пакеты состоят из 5000 операторов и 10 из них требуют перекомпиляции (так как это может произойти на уровне операторов), комп / сек будет в 10 раз больше, чем пакет / сек. Это проблема?
Аарон Бертран
@DanM Также вы должны принять любую рекомендацию об изменении базы данных или глобальных настроек с подразумеваемым отказом от ответственности, что это то, что вы должны тестировать, а не просто включать, потому что кто-то в Интернете сказал, что это нужно сделать. :-) Я пытаюсь объяснить, как и почему может помочь изменение, но я не всегда помню, что нужно сказать очевидное: сначала проверьте его .
Аарон Бертран
Я понимаю вашу точку зрения - то, что составляет "высокие" компиляции, полностью зависит от окружающей среды.
ДанМ
-1

До сих пор я видел наиболее типичную причину появления этих ожиданий в результате фрагментированных или недостаточных индексов и статистики, которая либо имеет недостаточный размер выборки, либо устарела. Это приводит к массивному сканированию полной таблицы, в результате чего происходит переполнение всей памяти, что, в свою очередь, приводит к появлению симптома, который мы часто видим как RESOURCE_SEMAPHORE_QUERY_COMPILE.

Самый простой способ проверить это - проверить, выполняют ли запросы полное сканирование таблицы / сканирование индекса, когда они должны выполнять поиск индекса. Если у вас есть проблемный запрос, с помощью которого вы можете воспроизвести проблему - становится очень легко диагностировать и исправить это.

Я бы проверил индексы на таблицах, затронутых этими проблемными запросами - т.е. проверьте фрагментацию индекса, потенциальные отфильтрованные индексы, которые не используются, отсутствующие индексы, которые вы, возможно, захотите создать, и т. д. Также обновите их статистику с помощью FULLSCAN как можно скорее.

Следует помнить, что ваша таблица проблем может быть не единственной, которая нуждается в этом. Например, если у вас есть запрос, который выбирает данные из 10 таблиц, планировщик выполнения может иногда показывать, что он не использует индекс в таблице 1, но когда вы затем проверяете индекс в таблице 1, это на самом деле нормально. Планировщик запросов может разрешить выборку данных из таблицы 1 с полным сканированием таблицы, поскольку, например, из-за неверного / недостаточного индекса в таблице 7 было возвращено так много данных, что это был бы более быстрый вариант. Так что диагностировать их иногда бывает сложно.

Кроме того, если у вас есть много запросов с выделенным кодом, например, с несколькими изменениями значений переменных, вы можете рассмотреть возможность оптимизации для специальных рабочих нагрузок . По сути, он сохраняет заглушку скомпилированного плана вместо всего плана, экономя ресурсы, когда вы никогда не получаете точно одинаковые планы каждый раз.

Кан
источник
Большая часть того, что вы выделите, может привести к неэффективным планам запросов, а не к большим временам компиляции. ИМХО.
Аарон Бертран
Тем не менее, я видел, как это происходило несколько раз, и это ожидание часто было именно тем, что последовало. Очевидно, я понятия не имею, насколько это распространено или применимо здесь, но методы, упомянутые выше, исправили это.
Кан
В качестве последующего редактирования в нашем случае оно появилось в довольно большой БД после того, как были затронуты критические индексы / статистика, которые использовались большим количеством запросов, выполняемых все время.
Кан
1
Правильно, и ожидание компиляции пришло, потому что индексы / статистика были изменены, что привело к перекомпиляции всех соответствующих планов, а не потому, что они были фрагментированы или устарели (как указано в ваших ответах).
Аарон Бертран