RESOURCE_SEMAPHORE_QUERY_COMPILE intermittente Attendi statistiche


8

Sto cercando di risolvere alcuni picchi intermittenti di CPU a cui stiamo assistendo in uno dei nostri server SQL di produzione. Stiamo eseguendo SQL Server 2008 R2 Standard Edition con 28 GB RAM e 4 core CPU. Quando ciò accade, notiamo un gran numero di attese di RESOURCE_SEMAPHORE_QUERY_COMPILER, che dura circa un minuto o due e quindi si interrompe, quindi l'utilizzo della CPU torna alla normalità.

Dopo aver studiato questo, capisco che questo è normalmente causato dalla compilazione di molti piani di esecuzione non riutilizzabili, che stiamo attualmente lavorando su modifiche alla nostra applicazione da affrontare.

Questo comportamento può essere attivato anche dagli sfratti della cache del piano dovuti alla pressione della memoria? In tal caso, come verificherei questo? Sto cercando di vedere se ci sono rimedi a breve termine che possiamo fare, come l'aggiornamento della RAM del server, fino a quando non implementiamo le nostre correzioni dell'applicazione. L'unica altra opzione a breve termine che mi viene in mente è quella di spostare alcuni dei nostri database più occupati su server diversi.

Risposte:


6

Credo che vedrai questo sintomo se hai MOLTI piani di query di grandi dimensioni che stanno lottando per la memoria per la compilazione (questo ha ben poco a che fare con l'esecuzione della query stessa). Per colpirlo, sospetto che tu stia utilizzando un ORM o qualche tipo di applicazione che genera molte query uniche ma relativamente complesse. SQL Server potrebbe essere sotto pressione della memoria a causa di operazioni come le query di grandi dimensioni, ma in più è più probabile che il tuo sistema sia configurato con molta meno memoria del necessario (o non c'è mai abbastanza memoria per soddisfare tutte le query che hai stai provando a compilare, o ci sono altri processi sulla scatola che stanno rubando memoria da SQL Server).

Puoi dare un'occhiata a cosa è configurato SQL Server usando:

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
  );

È possibile identificare i piani memorizzati nella cache che richiedono la memoria più compilata con la seguente query Jonathan Kehayias , adattata leggermente:

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);

Puoi vedere come viene utilizzata la cache del piano con quanto segue:

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;

Quando si verificano attese con semaforo elevato, verificare se i risultati di queste query variano in modo significativo durante l'attività "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;

E potresti anche voler guardare e vedere come viene distribuita la memoria:

DBCC MEMORYSTATUS;

E ci sono alcune buone informazioni qui sul motivo per cui potresti vedere un numero elevato di compilazioni / ricompilazioni (che contribuiranno a quell'attesa):

http://technet.microsoft.com/en-us/library/ee343986(v=sql.100).aspx

http://technet.microsoft.com/en-us/library/cc293620.aspx

È possibile verificare i conteggi di compilazione / ricompilazione elevati utilizzando i seguenti contatori:

SELECT counter_name, cntr_value
  FROM sys.dm_os_performance_counters
  WHERE counter_name IN 
  (
    'SQL Compilations/sec',
    'SQL Re-Compilations/sec'
  );

E puoi verificare la pressione della memoria interna che porta agli sfratti: i contatori diversi da zero qui indicano che qualcosa non va bene con la cache del piano:

SELECT * FROM sys.dm_os_memory_cache_clock_hands 
  WHERE [type] IN (N'CACHESTORE_SQLCP', N'CACHESTORE_OBJCP');

NOTA La maggior parte di queste metriche non ha una magia "oh mio Dio, devo andare nel panico o fare qualcosa!" soglia. Quello che devi fare è prendere le misure durante la normale attività del sistema e determinare dove si trovano queste soglie per l'hardware, la configurazione e il carico di lavoro. Quando ti fai prendere dal panico qualcosa succede quando due condizioni sono vere:

  1. le metriche variano significativamente dai valori normali; e,
  2. si verifica effettivamente un problema di prestazioni (come i picchi della CPU), ma solo se interferiscono effettivamente con qualcosa. Oltre a vedere il picco delle CPU, vedi qualche altro sintomo? In altre parole, il picco è il sintomo o il picco sta causando altri sintomi? Gli utenti del sistema lo noterebbero mai? Molte persone cercano sempre il consumatore più in attesa, semplicemente perché è il più alto. Qualcosa sarà sempre il consumatore più in attesa: devi sapere che varia abbastanza dalla normale attività da indicare un problema o qualche cambiamento significativo.

Optimize for ad hoc workloadsè un'ottima impostazione per il 99% dei carichi di lavoro disponibili, ma non sarà molto utile per ridurre i costi di compilazione: mira a ridurre il gonfiore della cache del piano impedendo a un piano monouso di archiviare l'intero piano fino a quando non viene eseguito due volte . Anche quando si memorizza solo lo stub nella cache del piano, è comunque necessario compilare il piano completo per l'esecuzione della query. Forse ciò che @Kahn intendeva raccomandare era impostare la parametrizzazione a livello di database su forzata , che potenzialmente fornirà un migliore riutilizzo del piano (ma dipende davvero da quanto siano uniche tutte queste query ad alto costo).

Anche alcune buone informazioni in questo white paper sulla cache e la compilazione del piano.


Al momento abbiamo il Optimize for ad hoc workloadsset, anche se, come hai detto, non è davvero rilevante per questo particolare problema. Abbiamo un codice che genera molte query uniche, alcune da uno strumento ORM, altre codificate a mano. Per quanto ne so, i picchi della CPU non si verificano abbastanza a lungo da essere notati dai nostri utenti. L'impostazione del database sulla parametrizzazione forzata mi sembra pericolosa.
DanM,

Una domanda: quando controlli le compilation alte, che cosa costituisce effettivamente un numero elevato? Ho pensato che le compilazioni / sec fossero significative solo se confrontate con il numero di richieste batch / sec.
DanM,

@DanM come ho detto sopra, non ho modo di sapere cosa potrebbe essere alto per il tuo ambiente, perché non ho idea di cosa sia normale per il tuo ambiente. Se il numero è vicino o superiore al numero di richieste batch / sec, questo può essere un indicatore, ma di nuovo dipende. Ad esempio, se i batch sono costituiti da 5000 istruzioni e 10 di esse richiedono ricompilazioni (poiché ciò può avvenire a livello di istruzione), comp / sec sarà 10x rispetto a batch / sec. È un problema?
Aaron Bertrand

@DanM Inoltre, dovresti prendere qualsiasi raccomandazione per cambiare un database o un'impostazione globale con un disclaimer implicito che è qualcosa che dovresti testare e non solo abilitare perché qualcuno su Internet ha detto di farlo. :-) Cerco di spiegare come e perché un cambiamento può aiutare, ma non ricordo sempre di affermare l'ovvio: provalo prima .
Aaron Bertrand

Vedo il tuo punto: ciò che costituisce compilazioni "alte" dipende interamente dall'ambiente.
DanM,

-1

Il motivo più tipico di gran lunga che ho visto apparire quelle attese, sono il risultato di indici frammentati o insufficienti e statistiche che hanno dimensioni del campione insufficienti o sono obsolete. Ciò si traduce in enormi scansioni della tabella completa che occupano tutta la memoria, che a sua volta produce un sintomo che spesso vediamo come RESOURCE_SEMAPHORE_QUERY_COMPILE.

Il modo più semplice per verificarlo è verificare se le query eseguono scansioni di tabelle complete / scansioni dell'indice, quando dovrebbero eseguire ricerche di indice. Se hai una domanda con cui puoi riprodurre il problema, diventa molto facile diagnosticare e risolvere questo problema.

Verificherei gli indici sui tavoli interessati da quelle domande problematiche - vale a dire. controlla la frammentazione dell'indice, i potenziali indici filtrati che non vengono utilizzati, gli indici mancanti che potresti voler creare, ecc. Inoltre, aggiorna le loro statistiche con FULLSCAN il prima possibile.

Un buon punto da ricordare è che la tua tabella dei problemi potrebbe non essere l'unica a averne bisogno. Ad esempio, se si dispone di una query che recupera i dati da 10 tabelle, il pianificatore di esecuzione può occasionalmente mostrare che non utilizza l'indice sulla tabella 1, ma quando si controlla l'indice sulla tabella 1, in realtà è ok. Il pianificatore di query potrebbe decidere di recuperare correttamente i dati sulla tabella 1 con una scansione completa della tabella, ad esempio perché un indice difettoso / insufficiente sulla tabella 7 ha restituito così tanti dati che questa sarebbe l'opzione più veloce. Quindi la diagnosi di questi a volte può essere complicata.

Inoltre, se hai un sacco di codebehind query con poche modifiche ai valori delle variabili, ad esempio, potresti voler esaminare l' ottimizzazione per i carichi di lavoro ad hoc . Fondamentalmente quello che fa è archiviare uno stub del piano compilato anziché dell'intero piano, risparmiando risorse quando non si ottengono mai esattamente gli stessi piani ogni volta.


La maggior parte delle cose evidenziate causerebbe piani di query inefficienti, non tempi di compilazione elevati. A PARER MIO.
Aaron Bertrand

Tuttavia, l'ho visto accadere più volte e quell'attesa è stata spesso esattamente ciò che è seguito. Ovviamente non ho idea di quanto sia comune o se si applica qui, ma i metodi sopra menzionati lo hanno risolto.
Kahn,

Come modifica successiva, nel nostro caso è apparso in un DB piuttosto grande dopo che sono stati interessati gli indici / le statistiche critici che sono stati utilizzati da un gran numero di query in esecuzione tutto il tempo.
Kahn,

1
Giusto, e le attese di compilazione sono arrivate perché gli indici / le statistiche sono stati cambiati, il che ha causato la ricompilazione di tutti i piani pertinenti, non perché frammentati o obsoleti (come indicato nella risposta).
Aaron Bertrand
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.