Prestazioni di SQL Server: PREEMPTIVE_OS_DELETESECURITYCONTEXT tipo di attesa dominante


8

Ieri ho ricevuto una chiamata da un cliente che si lamentava dell'elevato utilizzo della CPU sul proprio SQL Server. Stiamo utilizzando SQL Server 2012 64 bit SE. Il server esegue Windows Server 2008 R2 Standard, Intel Xeon a 2,20 GHz (4 core), 16 GB di RAM.

Dopo essermi accertato che il colpevole fosse proprio SQL Server, ho dato un'occhiata alle prime attese per l'istanza utilizzando la query DMV qui . Le prime due attese erano: (1) PREEMPTIVE_OS_DELETESECURITYCONTEXTe (2) SOS_SCHEDULER_YIELD.

EDIT : Ecco il risultato della "query top waitits" (anche se qualcuno ha riavviato il server questa mattina contro i miei desideri):

inserisci qui la descrizione dell'immagine

Facciamo molti calcoli / conversioni intense, quindi posso capire SOS_SCHEDULER_YIELD. Tuttavia, sono molto curioso del PREEMPTIVE_OS_DELETESECURITYCONTEXTtipo di attesa e del perché potrebbe essere il più alto.

La migliore descrizione / discussione che posso trovare su questo tipo di attesa può essere trovata qui . Menziona:

I tipi di attesa PREEMPTIVE_OS_ sono chiamate che hanno lasciato il motore di database, in genere a un'API Win32, e eseguono codice all'esterno di SQL Server per varie attività. In questo caso, sta eliminando un contesto di sicurezza precedentemente utilizzato per l'accesso remoto alle risorse. L'API correlata è in realtà denominata DeleteSecurityContext ()

Per quanto ne so, non abbiamo risorse esterne come server collegati o filetable. E non facciamo alcuna imitazione, ecc. Un backup potrebbe aver causato un picco o forse un controller di dominio difettoso?

Che diamine potrebbe causare questo tipo di attesa dominante? Come posso monitorare ulteriormente questo tipo di attesa?

Modifica 2: ho controllato il contenuto del registro di sicurezza di Windows. Vedo alcune voci che potrebbero essere di interesse, ma non sono sicuro che siano normali:

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLServerOLAPService
    Account Name:       MSSQLServerOLAPService
    Account Domain:     NT Service
    Logon ID:       0x3143c

Privileges:     SeImpersonatePrivilege

Special privileges assigned to new logon.

Subject:
    Security ID:        NT SERVICE\MSSQLSERVER
    Account Name:       MSSQLSERVER
    Account Domain:     NT Service
    Logon ID:       0x2f872

Privileges:     SeAssignPrimaryTokenPrivilege
            SeImpersonatePrivilege

Modifica 3 : @Jon Seigel, come richiesto, ecco i risultati della tua query. Un po 'diverso da quello di Paul:

inserisci qui la descrizione dell'immagine

Modifica 4: lo ammetto, sono un utente di Extended Events per la prima volta. Ho aggiunto questo tipo di attesa all'evento wait_info_external e ho visto centinaia di voci. Non c'è testo sql o handle di piano, solo uno stack di chiamate. Come posso rintracciare ulteriormente la fonte?

inserisci qui la descrizione dell'immagine


John, puoi per favore eseguire sp_whoisactive per un periodo di tempo (forse un minuto) e vedere cosa appare? Questo può aiutarti a indirizzare te / noi verso una soluzione. sqlblog.com/files/default.aspx
Mark Wilkinson

Ciao John, nella tua domanda accenni di aver identificato SQL Server come colpevole. Potresti per favore descrivere i passi che hai preso per giungere a quella conclusione?
Craig Efrein,

Risposte:


3

So che questa domanda, basata sul titolo, riguarda principalmente il tipo di attesa PREEMPTIVE_OS_DELETESECURITYCONTEXT, ma credo che sia un errore nella direzione del vero problema che è " un cliente che si lamentava dell'elevato utilizzo della CPU sul proprio SQL Server ".

Il motivo per cui credo che concentrarsi su questo specifico tipo di attesa sia una caccia all'oca selvatica è perché aumenta per ogni connessione stabilita. Sto eseguendo la seguente query sul mio laptop (nel senso che sono l'unico utente):

SELECT * 
FROM sys.dm_os_wait_stats
WHERE wait_type = N'PREEMPTIVE_OS_DELETESECURITYCONTEXT'

Quindi eseguo una delle seguenti operazioni ed eseguo nuovamente questa query:

  • aprire una nuova scheda query
  • chiudere la nuova scheda della query
  • eseguire quanto segue da un prompt di DOS: SQLCMD -E -Q "select 1"

Ora sappiamo che la CPU è alta, quindi dovremmo guardare cosa è in esecuzione per vedere quali sessioni hanno CPU alta:

SELECT req.session_id AS [SPID],
       req.blocking_session_id AS [BlockedBy],
       req.logical_reads AS [LogReads],
       DB_NAME(req.database_id) AS [DatabaseName],
       SUBSTRING(txt.[text],
                 (req.statement_start_offset / 2) + 1,
                 CASE
                     WHEN req.statement_end_offset > 0
                        THEN (req.statement_end_offset - req.statement_start_offset) / 2
                     ELSE LEN(txt.[text])
                 END
                ) AS [CurrentStatement],
       txt.[text] AS [CurrentBatch],
       CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
       OBJECT_NAME(qplan.objectid, qplan.[dbid]) AS [ObjectName],
       sess.[program_name],
       sess.[host_name],
       sess.nt_user_name,
       sess.total_scheduled_time,
       sess.memory_usage,
       req.*
FROM sys.dm_exec_requests req
INNER JOIN sys.dm_exec_sessions sess
        ON sess.session_id = req.session_id
CROSS APPLY sys.dm_exec_sql_text(req.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(req.plan_handle,
                                        req.statement_start_offset,
                                        req.statement_end_offset) qplan
WHERE req.session_id <> @@SPID
ORDER BY req.logical_reads DESC, req.cpu_time DESC
--ORDER BY req.cpu_time DESC, req.logical_reads DESC

Di solito eseguo la query sopra così com'è, ma potresti anche cambiare quale clausola ORDER BY è commentata per vedere se ciò dà risultati più interessanti / utili.

In alternativa, è possibile eseguire quanto segue, basato su dm_exec_query_stats, per trovare le query più costose. La prima query di seguito ti mostrerà le singole query (anche se hanno più piani) ed è ordinata in base al tempo medio della CPU, ma puoi facilmente cambiarla in Letture logiche medie. Quando trovi una query che sembra richiedere molte risorse, copia "sql_handle" e "statement_start_offset" nella condizione WHERE della seconda query in basso per vedere i singoli piani (può essere più di 1). Scorri verso l'estrema destra e supponendo che esistesse un piano XML, verrà visualizzato come un collegamento (in modalità griglia) che ti porterà al visualizzatore del piano se fai clic su di esso.

Query n. 1: ottieni informazioni sulla query

;WITH cte AS
(
   SELECT qstat.[sql_handle],
          qstat.statement_start_offset,
          qstat.statement_end_offset,
          COUNT(*) AS [NumberOfPlans],
          SUM(qstat.execution_count) AS [TotalExecutions],

          SUM(qstat.total_worker_time) AS [TotalCPU],
          (SUM(qstat.total_worker_time * 1.0) / SUM(qstat.execution_count)) AS [AvgCPUtime],
          MAX(qstat.max_worker_time) AS [MaxCPU],

          SUM(qstat.total_logical_reads) AS [TotalLogicalReads],
   (SUM(qstat.total_logical_reads * 1.0) / SUM(qstat.execution_count)) AS [AvgLogicalReads],
          MAX(qstat.max_logical_reads) AS [MaxLogicalReads],

          SUM(qstat.total_rows) AS [TotalRows],
          (SUM(qstat.total_rows * 1.0) / SUM(qstat.execution_count)) AS [AvgRows],
          MAX(qstat.max_rows) AS [MaxRows]
   FROM sys.dm_exec_query_stats  qstat
   GROUP BY qstat.[sql_handle], qstat.statement_start_offset, qstat.statement_end_offset
)
SELECT  cte.*,
        DB_NAME(txt.[dbid]) AS [DatabaseName],
        SUBSTRING(txt.[text],
                  (cte.statement_start_offset / 2) + 1,
                  CASE
                      WHEN cte.statement_end_offset > 0
                          THEN (cte.statement_end_offset - cte.statement_start_offset) / 2
                      ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM cte
CROSS APPLY sys.dm_exec_sql_text(cte.[sql_handle]) txt
ORDER BY cte.AvgCPUtime DESC

Query n. 2: ottenere informazioni sul piano

SELECT  *,
        DB_NAME(qplan.[dbid]) AS [DatabaseName],
        CONVERT(XML, qplan.query_plan) AS [StatementQueryPlan],
        SUBSTRING(txt.[text],
                  (qstat.statement_start_offset / 2) + 1,
                  CASE
                        WHEN qstat.statement_end_offset > 0
                        THEN (qstat.statement_end_offset - qstat.statement_start_offset) / 2
                        ELSE LEN(txt.[text])
                  END
                 ) AS [CurrentStatement],
        txt.[text] AS [CurrentBatch]
FROM sys.dm_exec_query_stats  qstat
CROSS APPLY sys.dm_exec_sql_text(qstat.[sql_handle]) txt
OUTER APPLY sys.dm_exec_text_query_plan(qstat.plan_handle,
                                        qstat.statement_start_offset,
                                        qstat.statement_end_offset) qplan
-- paste info from Query #1 below
WHERE qstat.[sql_handle] = 0x020000001C70C614D261C85875D4EF3C90BD18D02D62453800....
AND qstat.statement_start_offset = 164
-- paste info from Query #1 above
ORDER BY qstat.total_worker_time DESC

Ho pensato che il tipo di attesa più alto nella sceneggiatura di Paul, ovvero PREEMPTIVE_OS_DELETESECURITYCONTEXT, potesse essere la causa dell'alta CPU. Questo può tranquillamente essere considerato un tipo di attesa benigna nel nostro caso? Nella nostra applicazione, abbiamo alcuni servizi di Windows che inviano costantemente comandi (exec stored procs) a SQL Server. Non riesco a discernere troppi schemi da sys.dm_exec_sessions - le sessioni non rimangono aperte troppo a lungo e ce ne sono molte. sys.dm_exec_query_stats fornisce alcune buone informazioni sui proc memorizzati più costosi per quanto riguarda il costo complessivo della CPU. Questo potrebbe essere un buon punto di partenza.
John Russell,

Volevo solo assicurarmi che non mi mancasse qualcosa con PREEMPTIVE_OS_DELETESECURITYCONTEXT. Non sapevo se questo potesse essere ricondotto a un controller di dominio o a ricerche AD difettose?
John Russell,

@JohnRussell: Penso che il tipo di attesa più alto sia in genere un buon punto di partenza, ma il mio punto è che questo particolare non è solo attivato da codice all'interno di SQL Server che accede a risorse esterne, come Linked Server o SQLCLR o estesi processi memorizzati (ad es. xp_dirtree), quindi un volume elevato non è un vero indicatore. E anche se la latenza della rete causa ritardi, ciò aumenterebbe davvero la CPU o aumenterebbe il blocco? E buon punto, usa query_stats. In seguito aggiornerò la mia domanda.
Solomon Rutzky,

1
@JohnRussell: riguardo ai tuoi "servizi Windows che inviano costantemente comandi", qualcosa è cambiato di recente? Stanno chiudendo correttamente le connessioni? Puliscono correttamente la connessione se si verificano errori durante la connessione? Inoltre, hai ricostruito gli indici di recente o almeno aggiornato le statistiche su tutte le tabelle? Non farlo potrebbe portare ad un aumento della CPU.
Solomon Rutzky,

Grazie per la comprensione! Mentre osservo più da vicino sys.dm_exec_query_stats e la frammentazione dell'indice su alcune tabelle chiave, sto iniziando a sentirmi più sicuro della causa. PREEMPTIVE_OS_DELETESECURITYCONTEXT mi ha appena respinto.
John Russell,

1

SecurityContext viene utilizzato dal server sql in diversi punti. Un esempio che hai nominato sono i server collegati e i filetable. Forse stai usando cmdexec? Lavori di SQL Server Agent con account proxy? Chiamare un servizio web? Le risorse remote possono essere molte cose divertenti.

Gli eventi di rappresentazione possono essere registrati nell'evento di sicurezza di Windows. Potrebbe essere che stai trovando un indizio lì. Inoltre, potresti voler controllare il registratore blackbox o eventi estesi.

Hai verificato se questi tipi di attesa sono nuovi (e in connessione con la CPU alta) o semplicemente normali per il tuo server?


Non abbiamo alcun lavoro di SQL Server Agent né WebServices. Ho cancellato le statistiche di attesa e rieseguito la query originale sopra e riemergono statistiche simili. Mi ci è voluto un po 'per capire come riconfigurare la sessione dell'evento esteso system_health per includere wait_info_external per waittype =' PREEMPTIVE_OS_DELETESECURITYCONTEXT ', ma alla fine l'ho aggiunto, riesco a vedere centinaia di questi eventi nei pochi secondi in cui osservavo i dati live . Sto cercando come decifrare meglio la fonte. Qualche consiglio su come rintracciarlo?
John Russell,
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.