È questo un sintomo di un server sovraccarico?


12

Ho provato a diagnosticare rallentamenti in un'applicazione. Per questo ho registrato gli eventi estesi di SQL Server .

  • Per questa domanda sto guardando una particolare procedura memorizzata.
  • Ma ci sono una serie base di una dozzina di procedure memorizzate che possono ugualmente essere usate come un'indagine da mela a mela
  • e ogni volta che eseguo manualmente una delle procedure memorizzate, funziona sempre velocemente
  • e se un utente riprova: funzionerà velocemente.

I tempi di esecuzione della procedura memorizzata variano notevolmente. Molte esecuzioni di questa procedura memorizzata ritornano in <1s:

inserisci qui la descrizione dell'immagine

E per quel secchio "veloce" , è molto meno di 1 secondo. In realtà sono circa 90 ms:

inserisci qui la descrizione dell'immagine

Ma c'è una lunga coda di utenti che devono aspettare 2 secondi, 3 secondi, 4 secondi. Alcuni devono aspettare 12, 13, 14 secondi. Poi ci sono anime davvero povere che devono aspettare 22, 23, 24.

E dopo 30 anni, l'applicazione client si arrende, interrompe la query e l'utente ha dovuto attendere 30 secondi .

Correlazione per trovare la causalità

Quindi ho cercato di correlare:

  • durata vs letture logiche
  • durata vs letture fisiche
  • durata vs tempo cpu

E nessuno sembra dare alcuna correlazione; nessuno sembra essere la causa

  • durata vs letture logiche : che siano un po 'o molte letture logiche, la durata oscilla ancora selvaggiamente :

    inserisci qui la descrizione dell'immagine

  • durata vs letture fisiche : anche se la query non è stata pubblicata dalla cache e sono necessarie molte letture fisiche, non influisce sulla durata:

    inserisci qui la descrizione dell'immagine

  • durata vs tempo CPU : se la query ha impiegato 0 secondi di tempo CPU o 2,5 secondi di tempo CPU, le durate hanno la stessa variabilità:

    inserisci qui la descrizione dell'immagine

Bonus : ho notato che Durata v Letture fisiche e Durata v Tempo CPU sembrano molto simili. Questo è dimostrato se provo a correlare il tempo della CPU con le letture fisiche:

inserisci qui la descrizione dell'immagine

Risulta che un sacco di utilizzo della CPU proviene dall'I / O. Chi lo sapeva!

Quindi, se non c'è nulla sull'atto di eseguire la query in grado di tenere conto delle differenze nei tempi di esecuzione, ciò implica che è qualcosa di non correlato alla CPU o al disco rigido?

Se la CPU o il disco rigido fossero il collo di bottiglia; non sarebbe il collo di bottiglia?

Se ipotizziamo che fosse la CPU a costituire il collo di bottiglia; che la CPU non è alimentata per questo server:

  • quindi le esecuzioni che impiegano più tempo della CPU richiederebbero più tempo?
  • dal momento che devono completare con gli altri utilizzando la CPU sovraccarica?

Allo stesso modo per i dischi rigidi. Se ipotizziamo che il disco rigido fosse un collo di bottiglia; che i dischi rigidi non abbiano un through-put casuale sufficiente per questo server:

  • allora le esecuzioni che usano più letture fisiche non richiederebbero più tempo?
  • dal momento che devono completare con altri utilizzando l'I / O del disco rigido sovraccaricato?

La procedura memorizzata non esegue né scrive né richiede alcuna scrittura.

  • Di solito restituisce 0 righe (90%).
  • Occasionalmente restituisce 1 riga (7%).
  • Raramente restituirà 2 righe (1,4%).
  • E nei casi peggiori ha restituito più di 2 righe (una volta restituendo 12 righe)

Quindi non è come restituire un volume folle di dati.

Utilizzo della CPU del server

L' utilizzo del processore del server è in media dell'1,8% circa, con un picco occasionale fino al 18%, quindi non sembra che il carico della CPU sia un problema:

inserisci qui la descrizione dell'immagine

Quindi la CPU del server non sembra sovraccarica.

Ma il server è virtuale ...

Qualcosa al di fuori dell'universo?

L'unica cosa che posso immaginare è qualcosa che esiste al di fuori dell'universo del server.

  • se non sono letture logiche
  • e non sono letture fisiche
  • e non è l'utilizzo della CPU
  • e non è carico della CPU

E non è come se fossero i parametri della procedura memorizzata (perché emette la stessa query manualmente e non richiede 27 secondi - ci vogliono ~ 0 secondi).

Cos'altro potrebbe rendere conto che il server impiega a volte 30 secondi, anziché 0 secondi, per eseguire la stessa procedura memorizzata compilata.

  • posti di blocco?

È un server virtuale

  • l'host è sovraccarico?
  • un'altra macchina virtuale sullo stesso host?

Passando attraverso gli eventi estesi del server; non succede nient'altro in particolare quando una query richiede improvvisamente 20 secondi. Funziona bene, quindi decide di non funzionare bene:

  • 2 secondi
  • 1 secondo
  • 30 secondi
  • 3 secondi
  • 2 secondi

E non ci sono altri oggetti particolarmente faticosi che posso trovare. Non è durante il backup del registro delle transazioni ogni 2 ore.

Cos'altro potrebbe essere?

C'è qualcosa che posso dire oltre: "il server" ?

Modifica : correlato per ora del giorno

Mi sono reso conto di aver correlato le durate a tutto:

  • letture logiche
  • letture fisiche
  • uso della CPU

Ma l'unica cosa a cui non l'ho correlato era l' ora del giorno . Forse il backup del registro delle transazioni ogni 2 ore è un problema.

O forse i rallentamenti si verificano nei mandrini durante i checkpoint?

No:

inserisci qui la descrizione dell'immagine

Intel Xeon Gold Quad-core 6142.

Modifica: le persone stanno ipotizzando il piano di esecuzione della query

Le persone stanno ipotizzando che i piani di esecuzione delle query debbano essere diversi tra "veloce" e "lento". Non sono.

E possiamo vederlo immediatamente dall'ispezione.

Sappiamo che la durata della domanda più lunga non è dovuta a un piano di esecuzione "scadente":

  • uno che ha preso più letture logiche
  • uno che ha consumato più CPU da più join e ricerche chiave

Perché se un aumento delle letture o un aumento della CPU fosse una causa della maggiore durata della query, lo avremmo già visto sopra. Non c'è correlazione.

Ma proviamo a correlare la durata con la metrica del prodotto dell'area di lettura CPU:

inserisci qui la descrizione dell'immagine

La correlazione diventa ancora meno, il che è un paradosso.


Modifica : aggiornati i diagrammi a dispersione per aggirare un bug nei grafici a dispersione di Excel con un numero elevato di valori.

Prossimi passi

I miei prossimi passi saranno far sì che qualcuno debba server generare eventi per query bloccate - dopo 5 secondi:

EXEC sp_configure 'blocked process threshold', '5';
RECONFIGURE

Non spiegherà se le query vengono bloccate per 4 secondi. Ma forse tutto ciò che blocca una query per 5 secondi ne blocca anche una per 4 secondi.

I piani lenti

Ecco il piano lento delle due stored procedure in esecuzione:

  • `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'
  • `EXECUTE FindFrob @CustomerID = 7383, @StartDate = '20190725 04: 00: 00.000', @EndDate = '20190726 04: 00: 00.000'

La stessa procedura memorizzata, con gli stessi parametri, esegue back to back:

| Duration (us) | CPU time (us) | Logical reads | Physical reads | 
|---------------|---------------|---------------|----------------|
|    13,984,446 |        47,000 |         5,110 |            771 |
|     4,603,566 |        47,000 |         5,126 |            740 |

Chiamata 1:

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Chiama 2

|--Nested Loops(Left Semi Join, OUTER REFERENCES:([Contoso2].[dbo].[Frobs].[FrobGUID]) OPTIMIZED)
    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]))
    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([Contoso2].[dbo].[FrobTransactions].[RowNumber]) OPTIMIZED)
    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |--Nested Loops(Inner Join, OUTER REFERENCES:([tpi].[TransactionGUID]) OPTIMIZED)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[TransactionPatronInfo].[IX_TransactionPatronInfo_CustomerID_TransactionGUID] AS [tpi]), SEEK:([tpi].[CustomerID]=[@CustomerID]) ORDERED FORWARD)
    |    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[Transactions].[IX_Transactions_TransactionGUIDTransactionDate]), SEEK:([Contoso2].[dbo].[Transactions].[TransactionGUID]=[Contoso2].[dbo
    |    |    |    |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions2_MoneyAppearsOncePerTransaction]), SEEK:([Contoso2].[dbo].[FrobTransactions].[TransactionGUID]=[Contos
    |    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactions_RowNumber]), SEEK:([Contoso2].[dbo].[FrobTransactions].[RowNumber]=[Contoso2].[dbo].[Fin
    |    |--Clustered Index Seek(OBJECT:([Contoso2].[dbo].[Frobs].[PK_Frobs_FrobGUID]), SEEK:([Contoso2].[dbo].[Frobs].[FrobGUID]=[Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]),  WHERE:([Contos
    |--Filter(WHERE:([Expr1009]>(1)))
     |--Compute Scalar(DEFINE:([Expr1009]=CONVERT_IMPLICIT(int,[Expr1012],0)))
          |--Stream Aggregate(DEFINE:([Expr1012]=Count(*)))
           |--Index Seek(OBJECT:([Contoso2].[dbo].[FrobTransactions].[IX_FrobTransactins_OnFrobGUID]), SEEK:([Contoso2].[dbo].[FrobTransactions].[OnFrobGUID]=[Contoso2].[dbo].[Frobs].[LC

Ha senso che i piani siano identici; sta eseguendo la stessa procedura memorizzata, con gli stessi parametri.


2
Puoi pubblicare i piani di query - buona corsa contro cattiva corsa?
Kin Shah,

4
La mia prima ipotesi da qui sarebbe bloccare ...
Tibor Karaszi,

3
Chi l'ha sottovalutato? È una domanda molto dettagliata, accuratamente studiata, anche se mancano i piani di query! +1 da me!
Vérace,

4
Come sei giunto alla conclusione che i piani di query sono "identici"? Vuoi dire che hanno la stessa forma? Pubblicali da qualche parte in modo che possiamo confrontare anche noi. Il solo fatto di dirci che sono identici non significa che siano identici.
Aaron Bertrand

3
L'aggiunta dei piani di esecuzione acuta utilizzando PasteThePlan potrebbe darci un'idea di cosa la query era in attesa.
Randi Vertongen,

Risposte:


2

Dai un'occhiata a wait_stats e mostrerà quali sono i maggiori colli di bottiglia sul tuo server SQL.

Di recente ho riscontrato un problema in cui un'applicazione esterna era intermittente in modo intermittente. L'esecuzione di stored procedure sul server stesso è stata sempre rapida.

Il monitoraggio delle prestazioni non ha mostrato nulla di cui preoccuparsi affatto con le cache SQL o l'utilizzo della RAM e l'IO sul server.

Ciò che ha aiutato a restringere l'indagine è stato interrogare le statistiche di attesa raccolte da SQL in sys.dm_os_wait_stats

L'eccellente script sul sito Web SQLSkills ti mostrerà quelli che stai vivendo di più. È quindi possibile restringere la ricerca per identificare le cause.

Una volta che sai quali sono le grandi attese, questo script aiuta a restringere la sessione / il database che sta vivendo le attese:

SELECT OSW.session_id,
       OSW.wait_duration_ms,
       OSW.wait_type,
       DB_NAME(EXR.database_id) AS DatabaseName
FROM sys.dm_os_waiting_tasks OSW
INNER JOIN sys.dm_exec_sessions EXS ON OSW.session_id = EXS.session_id
INNER JOIN sys.dm_exec_requests EXR ON EXR.session_id = OSW.session_id
OPTION(Recompile);

La query sopra e ulteriori dettagli provengono dal sito Web MSSQLTips .

La sp_BlitzFirstsceneggiatura del sito Web di Brent Ozar ti mostrerà anche cosa sta causando rallentamenti.

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.