Database
 sql >> Database >  >> RDS >> Database

Statistiche di attesa improvvisa:SOS_SCHEDULER_YIELD

Nel mio post precedente, ho discusso delle attese di LCK_M_XX, ASYNC_NETWORK_IO e OLEDB e delle reazioni istintive ad esse. In questo post continuerò con il tema delle statistiche di attesa e discuterò dell'attesa SOS_SCHEDULER_YIELD.

Quando SOS_SCHEDULER_YIELD è il più diffuso su un server, è comune vedere un utilizzo elevato e prolungato della CPU. La reazione istintiva qui è che il server deve essere sotto pressione della CPU o che il problema è uno spinlock.

Abbiamo bisogno di un po' di background qui per capire queste due reazioni.

Pianificazione dei thread

La pianificazione dei thread in SQL Server è gestita da SQL Server stesso, non da Windows (ovvero non è preventiva). La parte del sistema operativo SQL del motore di archiviazione fornisce funzionalità di pianificazione e transizione dei thread dall'esecuzione su un processore (in cui lo stato del thread è IN ESECUZIONE) all'essere nell'elenco di attesa in attesa che una risorsa diventi disponibile (lo stato è SOSPESO) all'essere nell'Eseguibile Metti in coda una volta che la risorsa diventa disponibile (lo stato è RUNNABLE) in attesa di arrivare in cima alla coda e di nuovo sul processore (torna allo stato RUNNING). Ho messo in maiuscolo Processore, Lista dei camerieri e Coda eseguibile per identificarli come parti di un programmatore.

Ogni volta che un thread ha bisogno di una risorsa che non può acquisire immediatamente, viene sospeso e attende nella lista dei camerieri che gli venga detto (segnalato) che la sua risorsa è disponibile. Il tempo trascorso nell'elenco dei camerieri è il tempo di attesa della risorsa e il tempo trascorso nella coda eseguibile è il tempo di attesa del segnale. Insieme si combinano per essere il tempo di attesa complessivo. SQL OS tiene traccia del tempo di attesa e del tempo di attesa del segnale, quindi dobbiamo fare un po' di calcoli sull'output di sys.dm_os_wait_stats per ricavare il tempo di attesa della risorsa (vedi il mio script qui).

La Waiter List non è ordinata (qualsiasi thread può essere segnalato in qualsiasi momento e spostato nella Runnable Queue) e la Runnable Queue è First-In-First-Out (FIFO) quasi il 100% delle volte. L'unica eccezione alla coda eseguibile che è FIFO è quando più gruppi di carico di lavoro di Resource Governor sono stati configurati nello stesso pool di risorse e hanno priorità diverse l'uno rispetto all'altro. Non l'ho mai visto usato con successo in produzione, quindi non ne discuterò ulteriormente.

C'è un altro motivo per cui un thread potrebbe dover essere spostato dal Processore:esaurisce il suo quantum. Il quantum del thread nel sistema operativo SQL è fissato a 4 millisecondi. Il thread stesso è responsabile di determinare che il suo quantum è stato esaurito (chiamando le routine di supporto nel sistema operativo SQL) e di rinunciare volontariamente al processore (noto come cedere). Quando ciò si verifica, il thread si sposta direttamente nella parte inferiore della coda eseguibile, poiché non c'è nulla da attendere. Tuttavia, il sistema operativo SQL deve registrare un tipo di attesa per questa transizione dal processore e registra SOS_SCHEDULER_YIELD.

Questo comportamento viene spesso scambiato per la pressione della CPU, ma non lo è:è solo un utilizzo sostenuto della CPU. La pressione della CPU e il suo riconoscimento sono un altro argomento per un post futuro. Per quanto riguarda questo post, fintanto che il tempo medio di attesa del segnale è basso (0-0,1-0,2 ms), è una scommessa abbastanza sicura che la pressione della CPU non sia un problema.

Spinlock

Uno spinlock è una primitiva di sincronizzazione di livello molto basso utilizzata per fornire l'accesso thread-safe alle strutture di dati in SQL Server che sono estremamente calde (molto volatili e a cui si accede e modificano incredibilmente frequentemente da più thread). Esempi di tali strutture sono l'elenco senza buffer in ciascuna parte del pool di buffer e l'array di ponderazioni a riempimento proporzionale per i file di dati in un filegroup.

Quando un thread deve acquisire uno spinlock, cerca di vedere se lo spinlock è libero e, in tal caso, lo acquisisce immediatamente (usando una primitiva in linguaggio assembly interbloccato come "test bit clear and set"). Se non è possibile acquisire lo spinlock, il thread tenta immediatamente di acquisirlo di nuovo, ancora e ancora, per un massimo di mille iterazioni, finché non indietreggia (dorme per un po'). Questo non viene registrato come alcun tipo di attesa, poiché il thread chiama semplicemente la funzione sleep() di Windows, ma può fare in modo che altri thread in attesa abbiano tempi di attesa del segnale ampi (10-20 ms+) poiché il thread inattivo rimane sul processore finché non ottiene lo spinlock.

Perché parlo di spinlock? Perché possono anche essere una causa dell'utilizzo elevato della CPU e c'è un malinteso sul fatto che gli spinlock siano una causa delle attese SOS_SCHEDULER_YIELD. Non lo sono.

Cause SOS_SCHEDULER_YIELD

Quindi c'è una causa per SOS_SCHEDULER_YIELD:un thread che esaurisce il suo quantum di pianificazione e istanze fortemente ricorrenti può far sì che SOS_SCHEDULER_YIELD sia l'attesa più diffusa insieme a un elevato utilizzo della CPU.

Non vedrai le attese SOS_SCHEDULER_YIELD mostrate nell'output di sys.dm_os_waiting_tasks, poiché il thread non è in attesa. Puoi vedere quale query sta generando le attese SOS_SCHEDULER_YIELD eseguendo una query su sys.dm_exec_requests e filtrando sulla colonna last_wait_type.

Ciò significa anche che quando vedi SOS_SCHEDULER_YIELD nell'output di sys.dm_os_wait_stats, l'attesa della risorsa sarà zero, perché in realtà non è stata in attesa. Ma ricorda che ciascuna di queste "attese" equivale a 4 ms di tempo di CPU accumulato per la query.

L'unico modo per dimostrare la causa delle attese SOS_SCHEDULER_YIELD è acquisire gli stack di chiamate di SQL Server quando si verifica quel tipo di attesa, utilizzando gli eventi estesi e i simboli di debug di Microsoft. Ho un post sul blog che descrive e mostra come eseguire quell'indagine, e c'è un ottimo whitepaper sugli spinlock e le indagini sugli spinlock che vale la pena leggere se sei interessato a quella profondità di interni.

Per il caso dell'esaurimento quantistico, questa non è la causa principale. È un ulteriore sintomo. Ora dobbiamo considerare perché un thread potrebbe esaurire ripetutamente il suo quantum.

Un thread può esaurire il proprio quantum solo quando può continuare a elaborare il codice SQL Server per 4 ms senza la necessità di una risorsa di proprietà di un altro thread:nessuna attesa per blocchi, latch di pagina, pagine di file di dati da leggere dal disco, allocazioni di memoria, crescita dei file, registrazione o la miriade di altre risorse di cui un thread potrebbe aver bisogno.

Il pezzo di codice più comune in cui può verificarsi l'esaurimento quantistico e accumulare grandi quantità di attese SOS_SCHEDULER_YIELD è la scansione di un indice/tabella in cui tutte le pagine dei file di dati necessarie sono in memoria e non c'è contesa per l'accesso a quelle pagine, quindi ecco cosa Ti incoraggio a cercare nei piani di query quando vedi SOS_SCHEDULER_YIELD come il tipo di attesa principale:scansioni di indici/tabelle grandi e/o ripetute.

Ciò non significa che sto dicendo che le scansioni di grandi dimensioni siano dannose, poiché potrebbe essere che il modo più efficiente per elaborare il carico di lavoro sia attraverso una scansione. Tuttavia, se le attese SOS_SCHEDULER_YIELD sono nuove e insolite e sono causate da scansioni di grandi dimensioni, è necessario esaminare il motivo per cui i piani di query utilizzano le scansioni. Forse qualcuno ha eliminato un indice critico non cluster, o le statistiche non sono aggiornate e quindi è stato scelto un piano di query errato, o forse un valore di parametro insolito è stato passato a una procedura memorizzata e il piano di query ha richiesto una scansione o una modifica del codice si è verificato senza supportare le aggiunte all'indice.

Riepilogo

Proprio come con altri tipi di attesa, capire esattamente cosa significa SOS_SCHEDULER_YIELD è la chiave per capire come risolverlo e se il comportamento è previsto a causa del carico di lavoro in elaborazione.

Per quanto riguarda le statistiche generali sull'attesa, puoi trovare ulteriori informazioni sull'utilizzo per la risoluzione dei problemi relativi alle prestazioni in:

  • Le mie serie di post sul blog SQLskills, a partire dalle statistiche di attesa, o per favore dimmi dove fa male
  • La mia libreria Tipi di attesa e classi Latch qui
  • Il mio corso di formazione online Pluralsight SQL Server:risoluzione dei problemi delle prestazioni utilizzando le statistiche di attesa
  • Consigliere prestazioni SQL Sentry

Nel prossimo articolo della serie, parlerò di un altro tipo di attesa che è una causa comune di reazioni istintive. Fino ad allora, buona risoluzione dei problemi!