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.