RESOURCE_SEMAPHORE_QUERY_COMPILE intermitente Estadísticas de espera


8

Estoy tratando de solucionar algunos picos de CPU intermitentes que estamos presenciando en uno de nuestros servidores SQL de producción. Estamos ejecutando SQL Server 2008 R2 Standard Edition con 28 GB de RAM y 4 núcleos de CPU. Cuando esto sucede, estamos notando una gran cantidad de esperas de RESOURCE_SEMAPHORE_QUERY_COMPILER, que duran aproximadamente un minuto o dos y luego se detiene, lo que luego el uso de la CPU vuelve a la normalidad.

Después de investigar esto, entiendo que esto normalmente se debe a la compilación de muchos planes de ejecución no reutilizables, que actualmente estamos trabajando en cambios en nuestra aplicación para abordar.

¿Puede este comportamiento también ser desencadenado por los desalojos de caché del plan debido a la presión de la memoria? Si es así, ¿cómo comprobaría esto? Estoy tratando de ver si hay algún remedio a corto plazo que podamos hacer, como actualizar la RAM del servidor, hasta que implementemos nuestras soluciones. La única otra opción a corto plazo que se me ocurre es mover algunas de nuestras bases de datos más ocupadas a un servidor diferente.

Respuestas:


6

Creo que verá este síntoma si tiene MUCHOS planes de consultas grandes que están luchando por la memoria para compilar (esto tiene muy poco que ver con ejecutar la consulta en sí). Para lograr esto, sospecho que está utilizando un ORM o algún tipo de aplicación que genera muchas consultas únicas pero relativamente complejas. SQL Server podría estar bajo presión de memoria debido a cosas como grandes operaciones de consulta, pero si se piensa más, es más probable que su sistema esté configurado con mucha menos memoria de la que necesita (o nunca hay suficiente memoria para satisfacer todas las consultas que está intentando compilar, o hay otros procesos en la caja que están robando memoria de SQL Server).

Puede echar un vistazo a lo que SQL Server está configurado con:

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
  );

Puede identificar los planes en caché que requieren la memoria más compilada con la siguiente consulta de Jonathan Kehayias , ligeramente adaptada:

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);

Puede ver cómo se usa el caché del plan con lo siguiente:

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;

Cuando experimente altas esperas de semáforos, verifique si los resultados de estas consultas varían significativamente durante la actividad "normal":

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;

Y también es posible que desee ver y ver cómo se distribuye la memoria:

DBCC MEMORYSTATUS;

Y aquí hay buena información sobre por qué podría estar viendo una gran cantidad de compilaciones / recompilaciones (lo que contribuirá a esa espera):

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

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

Puede verificar los altos conteos de compilación / recompilación utilizando los siguientes contadores:

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

Y puede verificar la presión de la memoria interna que conduce a los desalojos: los contadores distintos de cero aquí indicarían que algo no está sucediendo con el caché del plan:

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

NOTA La mayoría de estas métricas no tienen una magia "¡Oh, Dios mío, tengo que entrar en pánico o hacer algo!" límite. Lo que debe hacer es tomar medidas durante la actividad normal del sistema y determinar dónde están estos umbrales para su hardware, configuración y carga de trabajo. Cuando entras en pánico, hacer algo es cuando se cumplen dos condiciones:

  1. las métricas varían significativamente de los valores normales; y,
  2. En realidad, se está produciendo un problema de rendimiento (como los picos de la CPU), pero solo si realmente están interfiriendo con algo. Además de ver el pico de CPU, ¿estás viendo algún otro síntoma? En otras palabras, ¿el pico es el síntoma o el pico está causando otros síntomas? ¿Se darían cuenta los usuarios del sistema? Mucha gente siempre persigue a su mayor consumidor de espera, simplemente porque es el más alto. Algo siempre será el consumidor de mayor espera: debe saber que varía lo suficiente de la actividad normal como para indicar un problema o algún cambio significativo.

Optimize for ad hoc workloadses una excelente configuración para el 99% de las cargas de trabajo, pero no será muy útil para reducir los costos de compilación: tiene como objetivo reducir la acumulación de caché del plan al evitar que un plan de un solo uso almacene todo el plan hasta que se ejecute dos veces . Incluso cuando solo almacena el código auxiliar en la memoria caché del plan, aún tiene que compilar el plan completo para la ejecución de la consulta. Quizás lo que @Kahn quiso recomendar fue establecer la parametrización del nivel de la base de datos en forzada , lo que potencialmente proporcionará una mejor reutilización del plan (pero realmente depende de cuán únicas sean todas estas consultas de alto costo).

También hay buena información en este documento sobre el almacenamiento en caché y la compilación del plan.


Sin Optimize for ad hoc workloadsembargo, actualmente tenemos el conjunto, como usted mencionó, no es realmente relevante para este problema en particular. Tenemos código que genera muchas consultas únicas, algunas de una herramienta ORM, algunas codificadas a mano. Hasta donde sé, los picos de CPU no se producen el tiempo suficiente para que nuestros usuarios lo noten. Configurar la base de datos para la parametrización forzada me parece peligroso.
DanM

Una pregunta: cuando verifica compilaciones altas, ¿qué constituye realmente un número alto? Pensé que las compilaciones / seg solo eran significativas en comparación con la cantidad de solicitudes por lote / seg.
DanM

@DanM como dije anteriormente, no hay forma de que sepa qué podría ser alto para su entorno, porque no tengo idea de lo que es normal para su entorno. Si el número es cercano o superior al número de solicitudes por lote / segundo, puede ser un indicador, pero nuevamente depende. Por ejemplo, si sus lotes consisten en 5000 declaraciones y 10 de ellas requieren recompilaciones (ya que esto puede suceder a nivel de declaración), comp / seg será 10 veces mayor que el lote / seg. ¿Es eso un problema?
Aaron Bertrand

@DanM También debe tomar cualquier recomendación para cambiar una base de datos o una configuración global con un descargo de responsabilidad implícito de que es algo que debe probar y no solo habilitar porque alguien en Internet dijo que lo hiciera. :-) Intento explicar cómo y por qué un cambio puede ayudar, pero no siempre recuerdo decir lo obvio: pruébelo primero .
Aaron Bertrand

Comprendo su punto de vista: lo que constituye compilaciones "altas" depende completamente del entorno.
DanM

-1

La razón más típica por la que he visto aparecer esas esperas, es como resultado de índices fragmentados o insuficientes, y estadísticas que tienen un tamaño de muestra insuficiente o son obsoletas. Esto da como resultado escaneos masivos de tablas completas que acaparan toda la memoria, lo que a su vez produce un síntoma que a menudo vemos como RESOURCE_SEMAPHORE_QUERY_COMPILE.

La forma más fácil de verificar esto es verificar si las consultas ejecutan escaneos completos de tablas / escaneos de índice, cuando deberían estar haciendo búsquedas de índice. Si tiene una consulta de problema con la que puede reproducir el problema, se vuelve muy fácil diagnosticarlo y solucionarlo.

Verificaría los índices en las tablas afectadas por esas consultas problemáticas, es decir. verifique la fragmentación del índice, los índices filtrados potenciales que no se utilizan, los índices faltantes que desee crear, etc. Además, actualice sus estadísticas con FULLSCAN lo antes posible.

Un buen punto para recordar es que su tabla de problemas puede no ser la única que lo necesita. Por ejemplo, si tiene una consulta que obtiene datos de 10 tablas, el planificador de ejecución puede mostrar ocasionalmente que no está utilizando el índice en la tabla 1, pero cuando luego verifica el índice en la tabla 1, en realidad está bien. El planificador de consultas puede resolver buscar datos en la tabla 1 con un escaneo completo de la tabla correctamente, porque un índice defectuoso / insuficiente en la tabla 7, por ejemplo, devolvió tantos datos que esta sería la opción más rápida. Por lo tanto, diagnosticar esto a veces puede ser complicado.

Además, si tiene muchas consultas de código subyacente con solo unos pocos cambios en los valores de las variables, por ejemplo, puede considerar habilitar la optimización para cargas de trabajo ad hoc . Básicamente, lo que hace es almacenar un trozo del plan compilado en lugar de todo el plan, ahorrando recursos cuando nunca obtienes exactamente los mismos planes cada vez.


La mayoría de las cosas que resalta causaría planes de consulta ineficientes, no tiempos de compilación altos. EN MI HUMILDE OPINIÓN.
Aaron Bertrand

Sin embargo, he visto que esto suceda varias veces y que la espera a menudo ha sido exactamente lo que siguió. Obviamente no tengo idea de lo común que es o si se aplica aquí, pero los métodos mencionados anteriormente lo arreglaron.
Kahn

Como una edición posterior, en nuestro caso apareció en una base de datos bastante grande después de que los índices / estadísticas críticos se vieron afectados por una gran cantidad de consultas que se ejecutan todo el tiempo.
Kahn

1
Correcto, y las esperas de compilación se produjeron porque se modificaron los índices / estadísticas, lo que provocó que se volvieran a compilar todos los planes relevantes, no porque estuvieran fragmentados o desactualizados (como indica su respuesta).
Aaron Bertrand
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.