Ricerca infinita nel Query Store


10

Dirò fin dall'inizio che la mia domanda / problema è simile a questo precedente, ma dal momento che non sono sicuro se la causa o le informazioni di partenza è lo stesso, ho deciso di pubblicare la mia domanda con qualche dettaglio in più.

Problema a portata di mano:

  • a un'ora strana (verso la fine della giornata lavorativa) un'istanza di produzione inizia a comportarsi in modo irregolare:
    • CPU elevata per l'istanza (da una base di circa il 30% è passata a circa il doppio ed era ancora in crescita)
    • aumento del numero di transazioni / sec (anche se il caricamento dell'app non ha visto alcun cambiamento)
    • aumento del numero di sessioni inattive
    • strani eventi di blocco tra sessioni che non hanno mai mostrato questo comportamento (anche leggere sessioni senza commit causavano il blocco)
    • le prime attese per l'intervallo erano il blocco della pagina al 1 ° posto, con i blocchi che occupavano il 2 ° posto

Indagine iniziale:

  • usando sp_whoIsActive abbiamo visto che una query eseguita dal nostro strumento di monitoraggio decide di funzionare in modo estremamente lento e catturare molta CPU, cosa che prima non accadeva;
  • il suo livello di isolamento è stato letto senza commit;
  • abbiamo esaminato il piano in cui abbiamo visto numeri stravaganti: StatementEstRows = "3.86846e + 010" con circa 150 TB di dati stimati da restituire
  • sospettavamo che la causa fosse una funzionalità di monitoraggio delle query dello strumento di monitoraggio, quindi abbiamo disabilitato la funzione (abbiamo anche aperto un ticket con il nostro provider per verificare se sono a conoscenza di eventuali problemi)
  • da quel primo evento, è successo un paio di volte in più, ogni volta che uccidiamo la sessione, tutto torna alla normalità;
  • ci rendiamo conto che la query è estremamente simile a una delle query utilizzate da MS in BOL per il monitoraggio del Query Store - Query recentemente regredite nelle prestazioni (confrontando diversi punti nel tempo)
  • eseguiamo la stessa query manualmente e vediamo lo stesso comportamento (CPU utilizzata in costante aumento, in attesa di latch, blocchi imprevisti ecc.)

Query colpevole:

Select qt.query_sql_text, 
    q.query_id, 
    qt.query_text_id, 
    rs1.runtime_stats_id AS runtime_stats_id_1,
    interval_1 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi1.start_time), 
    p1.plan_id AS plan_1, 
    rs1.avg_duration AS avg_duration_1, 
    rs2.avg_duration AS avg_duration_2,
    p2.plan_id AS plan_2, 
    interval_2 = DateAdd(minute, -(DateDiff(minute, getdate(), getutcdate())), rsi2.start_time), 
    rs2.runtime_stats_id AS runtime_stats_id_2
From sys.query_store_query_text AS qt 
Inner Join sys.query_store_query AS q 
    ON qt.query_text_id = q.query_text_id 
Inner Join sys.query_store_plan AS p1 
    ON q.query_id = p1.query_id 
Inner Join sys.query_store_runtime_stats AS rs1 
    ON p1.plan_id = rs1.plan_id 
Inner Join sys.query_store_runtime_stats_interval AS rsi1 
    ON rsi1.runtime_stats_interval_id = rs1.runtime_stats_interval_id 
 Inner Join sys.query_store_plan AS p2 
    ON q.query_id = p2.query_id 
Inner Join sys.query_store_runtime_stats AS rs2 
    ON p2.plan_id = rs2.plan_id 
Inner Join sys.query_store_runtime_stats_interval AS rsi2 
    ON rsi2.runtime_stats_interval_id = rs2.runtime_stats_interval_id
Where rsi1.start_time > DATEADD(hour, -48, GETUTCDATE()) 
    AND rsi2.start_time > rsi1.start_time 
    AND p1.plan_id <> p2.plan_id
    AND rs2.avg_duration > rs1.avg_duration * 2
Order By q.query_id, rsi1.start_time, rsi2.start_time

Impostazioni e informazioni:

  • SQL Server 2016 SP1 CU4 Enterprise su un cluster Windows Server 2012R2
  • Archivio query abilitato e configurato come predefinito (nessuna impostazione modificata)
  • database importato da un'istanza di SQL 2005 (e ancora al livello di compatibilità 100)

Osservazione empirica:

  • a causa di statistiche estremamente stravaganti, abbiamo preso tutti gli oggetti * plan_persist ** utilizzati nel piano stimato errato (nessun piano reale ancora, causa l'interruzione della query) e controllato le statistiche, alcuni degli indici utilizzati nel piano non avevano alcuna statistica (DBCC SHOWSTATISTICS non ha restituito nulla, selezionare da sys.stats ha mostrato la funzione NULL stats_date () per alcuni indici

Soluzione rapida e sporca:

  • creare manualmente statistiche mancanti su oggetti di sistema relativi a Query Store o
  • forzare l'esecuzione della query utilizzando il nuovo CE (traceflag), che creerà / aggiornerà anche le statistiche necessarie o
  • modificare il livello di compatibilità del database su 130 (quindi per impostazione predefinita utilizzerà il nuovo CE)

Quindi, la mia vera domanda sarebbe:

Perché una query su Query Store potrebbe causare problemi di prestazioni sull'intera istanza? Siamo in un territorio di bug con Query Store?

PS: caricherò alcuni file (schermate di stampa, statistiche IO e piani) in breve tempo.

File aggiunti su Dropbox .

Piano 1 - piano iniziale stravagante stimato in produzione

Piano 2 - piano attuale, vecchio CE, in un ambiente di prova (stesso comportamento, stesse statistiche stravaganti)

Piano 3 - piano effettivo, nuovo CE, in un ambiente di prova


1
Abbiamo finito per disabilitare l'archivio di query, non siamo sicuri di quale fosse la causa principale (abbiamo avuto sicuramente più di 1 problema). Da parte mia, la CPU aumenterebbe tutto ciò che abbiamo fatto clic per visualizzare le statistiche dall'archivio query.
A_V

Risposte:


6

Come ho detto nella risposta, il test empirico ha mostrato che esistevano indici su oggetti di sistema sys.plan_persisted * senza alcuna (nessuna) statistica creata su di essi. Sospetto che ciò avvenga perché il database viene migrato da un'istanza di SQL 2005 e mantenuto per un po 'al livello di compatibilità 100, quindi il nuovo CE non è stato utilizzato.

Verifica dei conteggi delle righe:

Select count(1) from NoNameDB.sys.plan_persist_runtime_stats with (nolock) --60362   
Select count(1) from NoNameDB.sys.plan_persist_plan with (nolock) --1853    
Select count(1) from NoNameDB.sys.plan_persist_runtime_stats_interval with (nolock) --671    
Select count(1) from NoNameDB.sys.plan_persist_query with (nolock) --1091    
Select count(1) from NoNameDB.sys.plan_persist_query_text with (nolock) --911

Ciò ha dimostrato che le stime iniziali erano errate. Fatto con una connessione DAC, altrimenti le tabelle non sono disponibili per la query.

Controllo statistiche:

DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats_interval', plan_persist_runtime_stats_interval_cidx);    
DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats', plan_persist_runtime_stats_idx1);    
DBCC SHOW_STATISTICS ('sys.plan_persist_runtime_stats', plan_persist_runtime_stats_cidx);    
DBCC SHOW_STATISTICS ('sys.plan_persist_plan', plan_persist_plan_cidx);    
DBCC SHOW_STATISTICS ('sys.plan_persist_plan', plan_persist_plan_idx1);    
DBCC SHOW_STATISTICS ('sys.plan_persist_query', plan_persist_query_cidx)    
DBCC SHOW_STATISTICS ('sys.plan_persist_query_text', plan_persist_query_text_cidx);

Ciò ha dimostrato che alcuni indici avevano statistiche vuote (mancanti, nessuna, zero).

Correzione iniziale:

UPDATE STATISTICS sys.plan_persist_runtime_stats WITH fullscan;
UPDATE STATISTICS sys.plan_persist_plan WITH fullscan;
UPDATE STATISTICS sys.plan_persist_runtime_stats_interval WITH fullscan;
UPDATE STATISTICS sys.plan_persist_query WITH fullscan;
UPDATE STATISTICS sys.plan_persist_query_text WITH fullscan;

Questo tipo di correzione delle statistiche ha fatto terminare la query in 10-12 secondi.

Seconda correzione :

(verificato solo in un ambiente di test) e molto probabilmente quello corretto, poiché mostrava le migliori statistiche per la query, era di modificare il livello di compatibilità del database su 130. Il risultato finale era che la query terminava in circa 10-12 secondi con statistiche numeriche normali (10k righe).

Correzione intermedia :

DBCC TRACEON (2312) -- new CE

Alcuni aiuti correlati sulle statistiche nelle tabelle nascoste del sistema.


3

Il problema di fondo, che è visibile se si apre il piano effettivo in SSMS e si esamina l'utilizzo della CPU (o si esamina XML), è il nodo 32, un TVF. Il colpevole delle lente query del Query Store è l' accesso ripetuto ai TVF in memoria .

Costo TVF

Non importa quante righe vengono restituite da questi TVF, solo il numero di volte in cui si accede. La correzione sarà qualunque cosa tu possa fare per allontanare i tuoi piani dalla lettura più volte.

Sulla base del mio debug limitato (sia in termini di competenze che di tempo impiegato), la mia ipotesi è che l'intera memoria assegnata per il particolare componente in memoria dei dati del Query Store venga scansionata ad ogni esecuzione TVF. Non sono stato in grado di influenzare questa allocazione di memoria con sp_query_store_flush_dbo DBCC FREESYSTEMCACHE.

Soluzioni alternative di successo finora includono guide di piano, suggerimenti ( OPTION(HASH JOIN, LOOP JOIN)finora ha funzionato abbastanza bene per me) ed esecuzione delle query del Query Store su un nodo di sola lettura di una AG.

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.