Je crois que vous verrez ce symptôme si vous avez BEAUCOUP de plans de requête volumineux qui se battent pour la mémoire afin de compiler (cela a très peu à voir avec l'exécution de la requête elle-même). Pour cela, je soupçonne que vous utilisez un ORM ou une sorte d'application qui génère de nombreuses requêtes uniques mais relativement complexes. SQL Server peut être sous pression en raison de choses telles que les opérations de requête volumineuses, mais après réflexion, il est plus probable que votre système soit configuré avec beaucoup moins de mémoire qu'il n'en a besoin (soit il n'y a jamais assez de mémoire pour satisfaire toutes les requêtes que vous essayez de compiler, ou il y a d'autres processus sur la boîte qui volent la mémoire de SQL Server).
Vous pouvez jeter un œil à la configuration de SQL Server à l'aide de:
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
);
Vous pouvez identifier les plans mis en cache qui nécessitaient le plus de mémoire de compilation avec la requête Jonathan Kehayias suivante , légèrement adaptée:
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);
Vous pouvez voir comment le cache de plan est utilisé avec les éléments suivants:
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;
Lorsque vous rencontrez des attentes élevées de sémaphore, vérifiez si ces résultats de requête varient considérablement par rapport à une activité "normale":
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;
Et vous voudrez peut-être aussi regarder et voir comment la mémoire est distribuée:
DBCC MEMORYSTATUS;
Et il y a de bonnes informations ici sur les raisons pour lesquelles vous pourriez voir un nombre élevé de compilations / recompilations (ce qui contribuera à cette attente):
http://technet.microsoft.com/en-us/library/ee343986(v=sql.100).aspx
http://technet.microsoft.com/en-us/library/cc293620.aspx
Vous pouvez vérifier les nombres élevés de compilation / recompilation à l'aide des compteurs suivants:
SELECT counter_name, cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name IN
(
'SQL Compilations/sec',
'SQL Re-Compilations/sec'
);
Et vous pouvez vérifier la pression de la mémoire interne conduisant à des expulsions - des compteurs non nuls ici indiqueraient qu'il se passe quelque chose de mal avec le cache du plan:
SELECT * FROM sys.dm_os_memory_cache_clock_hands
WHERE [type] IN (N'CACHESTORE_SQLCP', N'CACHESTORE_OBJCP');
REMARQUE La plupart de ces mesures n'ont pas de magie "oh mon Dieu, j'ai besoin de paniquer ou de faire quelque chose!" seuil. Ce que vous devez faire est de prendre des mesures pendant l' activité normale du système et de déterminer où se trouvent ces seuils pour votre matériel, votre configuration et votre charge de travail. Lorsque vous paniquez , quelque chose se produit lorsque deux conditions sont remplies:
- les mesures varient considérablement des valeurs normales; et,
- il y a en fait un problème de performances (comme vos pics de processeur) - mais seulement s'ils interfèrent réellement avec quoi que ce soit. En plus de voir le pic des processeurs, voyez-vous un autre symptôme? En d'autres termes, la pointe est-elle le symptôme ou la pointe provoque-t-elle d'autres symptômes? Les utilisateurs du système le remarqueraient-ils jamais? Beaucoup de gens recherchent toujours leur consommateur qui attend le plus, simplement parce que c'est le plus élevé. Quelque chose sera toujours le consommateur qui attend le plus - vous devez savoir qu'il diffère suffisamment de l'activité normale pour indiquer un problème ou un changement significatif.
Optimize for ad hoc workloads
est un cadre idéal pour 99% des charges de travail, mais il ne sera pas très utile pour réduire les coûts de compilation - il vise à réduire le gonflement du cache du plan en empêchant un plan à usage unique de stocker le plan entier jusqu'à ce qu'il soit exécuté deux fois . Même lorsque vous ne stockez que le stub dans le cache du plan, vous devez toujours compiler le plan complet pour l'exécution de la requête. Peut-être que @Kahn voulait recommander de définir le paramétrage du niveau de la base de données sur forcé , ce qui pourrait potentiellement permettre une meilleure réutilisation du plan (mais cela dépend vraiment de la spécificité de toutes ces requêtes coûteuses).
Également quelques bonnes informations dans ce livre blanc sur la mise en cache et la compilation des plans.
Optimize for ad hoc workloads
ensemble, mais, comme vous l'avez mentionné, il n'est pas vraiment pertinent pour cette question particulière. Nous avons du code qui génère de nombreuses requêtes uniques, certaines à partir d'un outil ORM, d'autres codées à la main. Pour autant que je sache, les pics de CPU ne se produisent pas assez longtemps pour que nos utilisateurs le remarquent. Définir la base de données sur un paramétrage forcé me semble dangereux.