Ich glaube, Sie werden dieses Symptom sehen, wenn Sie viele große Abfragepläne haben, die um Speicher kämpfen, um kompiliert zu werden (dies hat sehr wenig mit dem Ausführen der Abfrage selbst zu tun). Ich vermute, Sie verwenden ein ORM oder eine Anwendung, die viele eindeutige, aber relativ komplexe Abfragen generiert. SQL Server könnte aufgrund umfangreicher Abfragevorgänge unter Speicherdruck stehen. Bei näherer Betrachtung ist es jedoch wahrscheinlicher, dass Ihr System mit weitaus weniger Speicher konfiguriert ist, als es benötigt (entweder ist nie genügend Speicher vorhanden, um alle von Ihnen gestellten Abfragen zu erfüllen versuchen zu kompilieren, oder es gibt andere Prozesse auf der Box, die Speicher von SQL Server stehlen.
Sie können sich ansehen, wie SQL Server konfiguriert ist, indem Sie Folgendes verwenden:
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
);
Mit der folgenden Jonathan Kehayias-Abfrage , die leicht angepasst wurde, können Sie die zwischengespeicherten Pläne identifizieren, für die der meiste Kompilierungsspeicher erforderlich ist :
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);
Sie können sehen, wie der Plan-Cache wie folgt verwendet wird:
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;
Überprüfen Sie bei hohen Semaphor-Wartezeiten, ob diese Abfrageergebnisse erheblich von den "normalen" Aktivitäten abweichen:
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;
Vielleicht möchten Sie auch sehen, wie der Speicher verteilt ist:
DBCC MEMORYSTATUS;
Und hier gibt es einige gute Informationen darüber, warum möglicherweise eine große Anzahl von Kompilierungen / Neukompilierungen angezeigt wird (was zu diesem Warten beiträgt):
http://technet.microsoft.com/en-us/library/ee343986(v=sql.100).aspx
http://technet.microsoft.com/en-us/library/cc293620.aspx
Mit den folgenden Zählern können Sie nach hohen Kompilierungs- / Neukompilierungszahlen suchen:
SELECT counter_name, cntr_value
FROM sys.dm_os_performance_counters
WHERE counter_name IN
(
'SQL Compilations/sec',
'SQL Re-Compilations/sec'
);
Und Sie können nach internem Speicherdruck suchen, der zu Räumungen führt. Zähler ungleich Null deuten darauf hin, dass mit dem Plan-Cache etwas nicht Gutes passiert:
SELECT * FROM sys.dm_os_memory_cache_clock_hands
WHERE [type] IN (N'CACHESTORE_SQLCP', N'CACHESTORE_OBJCP');
HINWEIS Die meisten dieser Metriken haben keine magische "Oh mein Gott, ich muss in Panik geraten oder etwas tun!" Schwelle. Sie müssen während der normalen Systemaktivität Messungen durchführen und feststellen, wo diese Schwellenwerte für Ihre Hardware, Konfiguration und Arbeitslast liegen. Wenn Sie in Panik geraten, tun Sie etwas, wenn zwei Bedingungen erfüllt sind:
- Die Metriken weichen erheblich von den normalen Werten ab. und,
- Es tritt tatsächlich ein Leistungsproblem auf (wie bei Ihren CPU-Spitzen) - aber nur, wenn sie tatsächlich irgendetwas stören. Sehen Sie außer der Spitze der CPUs ein anderes Symptom? Mit anderen Worten, ist die Spitze das Symptom oder verursacht die Spitze andere Symptome? Würden Benutzer des Systems jemals bemerken? Viele Leute streben immer nach ihrem höchsten Warteverbraucher, einfach weil es der höchste ist. Etwas wird immer der Verbraucher mit der höchsten Wartezeit sein - Sie müssen wissen, dass es so weit von der normalen Aktivität abweicht, dass es auf ein Problem oder eine signifikante Änderung hinweist.
Optimize for ad hoc workloads
ist eine großartige Einstellung für 99% der Workloads, aber sie ist nicht sehr hilfreich bei der Reduzierung der Kompilierungskosten. Sie zielt darauf ab, das Aufblähen des Plan-Cache zu reduzieren, indem verhindert wird, dass ein Einwegplan den gesamten Plan speichert, bis er zweimal ausgeführt wird . Auch wenn Sie den Stub nur im Plan-Cache speichern, müssen Sie den vollständigen Plan für die Ausführung der Abfrage kompilieren. Vielleicht wollte @Kahn empfehlen, die Parametrisierung auf Datenbankebene auf erzwungen zu setzen , was möglicherweise eine bessere Wiederverwendung des Plans ermöglicht (aber es hängt wirklich davon ab, wie einzigartig all diese kostenintensiven Abfragen sind).
In diesem Whitepaper finden Sie auch einige gute Informationen zum Plan-Caching und zur Kompilierung.
Optimize for ad hoc workloads
Set, aber wie Sie bereits erwähnt haben, ist es für dieses spezielle Problem nicht wirklich relevant. Wir haben Code, der viele eindeutige Abfragen generiert, einige von einem ORM-Tool, andere von Hand codiert. Soweit ich weiß, treten die CPU-Spitzen nicht lange genug auf, damit unsere Benutzer sie bemerken. Das Einstellen der Datenbank auf erzwungene Parametrisierung klingt für mich gefährlich.Der mit Abstand typischste Grund, warum ich diese Wartezeiten gesehen habe, sind fragmentierte oder unzureichende Indizes und Statistiken, die entweder eine unzureichende Stichprobengröße aufweisen oder veraltet sind. Dies führt zu massiven vollständigen Tabellenscans, die den gesamten Speicher belasten, was wiederum ein Symptom erzeugt, das wir häufig als RESOURCE_SEMAPHORE_QUERY_COMPILE sehen.
Der einfachste Weg, dies zu überprüfen, besteht darin, zu überprüfen, ob die Abfragen vollständige Tabellenscans / Indexscans ausführen, wenn sie Indexsuchen durchführen sollten. Wenn Sie eine Problemabfrage haben, mit der Sie das Problem reproduzieren können, ist es sehr einfach, dies zu diagnostizieren und zu beheben.
Ich würde die Indizes für die Tabellen überprüfen, die von diesen Problemabfragen betroffen sind - dh. Überprüfen Sie die Indexfragmentierung, mögliche gefilterte Indizes, die nicht verwendet werden, fehlende Indizes, die Sie möglicherweise erstellen möchten usw. Aktualisieren Sie außerdem ihre Statistiken so bald wie möglich mit FULLSCAN.
Ein guter Punkt, an den Sie sich erinnern sollten, ist, dass Ihre Problemtabelle möglicherweise nicht die einzige ist, die dies benötigt. Wenn Sie beispielsweise eine Abfrage haben, die Daten aus 10 Tabellen abruft, zeigt der Ausführungsplaner gelegentlich an, dass der Index für Tabelle 1 nicht verwendet wird. Wenn Sie dann jedoch den Index für Tabelle 1 überprüfen, ist dies tatsächlich in Ordnung. Der Abfrageplaner kann beschließen, Daten in Tabelle 1 mit einem vollständigen Tabellenscan korrekt abzurufen, da beispielsweise ein fehlerhafter / unzureichender Index in Tabelle 7 so viele Daten zurückgegeben hat, dass dies die schnellere Option wäre. Daher kann die Diagnose manchmal schwierig sein.
Wenn Sie beispielsweise viele Codebehind-Abfragen mit nur wenigen Änderungen der Variablenwerte haben, sollten Sie die Optimierung für Ad-hoc-Workloads aktivieren . Grundsätzlich wird ein Stub des kompilierten Plans anstelle des gesamten Plans gespeichert, wodurch Ressourcen gespart werden, wenn Sie nie jedes Mal genau dieselben Pläne erhalten.
quelle