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.De loin, la raison la plus typique pour laquelle j'ai vu ces attentes apparaître est le résultat d'index fragmentés ou insuffisants et de statistiques qui ont soit une taille d'échantillon insuffisante, soit sont obsolètes. Il en résulte des analyses de table complètes massives qui accaparent toute la mémoire, ce qui à son tour produit un symptôme que nous considérons souvent comme RESOURCE_SEMAPHORE_QUERY_COMPILE.
Le moyen le plus simple de vérifier cela est de vérifier si les requêtes exécutent des analyses de table / analyses d'index complètes, alors qu'elles devraient effectuer des recherches d'index. Si vous avez une requête de problème avec laquelle vous pouvez reproduire le problème - il devient très facile de diagnostiquer et de résoudre ce problème.
Je vérifierais les index sur les tables affectées par ces requêtes problématiques - ie. vérifiez la fragmentation des index, les index filtrés potentiels qui ne sont pas utilisés, les index manquants que vous voudrez peut-être créer, etc. De plus, mettez à jour leurs statistiques avec FULLSCAN dès que possible.
Un bon point à retenir est que votre table de problèmes n'est peut-être pas la seule à en avoir besoin. Par exemple, si vous avez une requête qui récupère les données de 10 tables, le planificateur d'exécution peut parfois montrer qu'il n'utilise pas l'index sur la table 1, mais lorsque vous vérifiez ensuite l'index sur la table 1, c'est en fait correct. Le planificateur de requêtes peut résoudre pour récupérer correctement les données sur la table 1 avec un balayage complet de la table, car un index défectueux / insuffisant sur la table 7 par exemple, a renvoyé tellement de données que ce serait l'option la plus rapide. Il est donc parfois difficile de les diagnostiquer.
De plus, si vous avez beaucoup de requêtes codebehind avec seulement quelques changements dans les valeurs des variables par exemple, vous voudrez peut-être envisager d' activer l'optimisation pour les charges de travail ad hoc . Fondamentalement, il stocke un talon du plan compilé au lieu du plan entier, économisant des ressources lorsque vous n'obtenez jamais exactement les mêmes plans à chaque fois.
la source