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:
- las métricas varían significativamente de los valores normales; y,
- 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 workloads
es 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.
Optimize for ad hoc workloads
embargo, 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.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.
fuente