Performances SQL Server: type d'attente dominant PREEMPTIVE_OS_DELETESECURITYCONTEXT


8

Hier, j'ai reçu un appel d'un client qui se plaignait d'une utilisation élevée du processeur sur son serveur SQL. Nous utilisons SQL Server 2012 64 bits SE. Le serveur exécute Windows Server 2008 R2 Standard, Intel Xeon 2,20 GHz (4 cœurs), 16 Go de RAM.

Après m'être assuré que le coupable était bien SQL Server, j'ai jeté un coup d'œil aux premières attentes pour l'instance à l'aide de la requête DMV ici . Les deux premières attentes étaient: (1) PREEMPTIVE_OS_DELETESECURITYCONTEXTet (2) SOS_SCHEDULER_YIELD.

EDIT : Voici le résultat de la "requête top attentes" (bien que quelqu'un ait redémarré le serveur ce matin contre mes souhaits):

entrez la description de l'image ici

Nous faisons beaucoup de calculs / conversion intenses, donc je peux comprendre SOS_SCHEDULER_YIELD. Cependant, je suis très curieux de savoir le PREEMPTIVE_OS_DELETESECURITYCONTEXTtype d'attente et pourquoi il pourrait être le plus élevé.

La meilleure description / discussion que je peux trouver sur ce type d'attente peut être trouvée ici . Il mentionne:

Les types d'attente PREEMPTIVE_OS_ sont des appels qui ont quitté le moteur de base de données, généralement vers une API Win32, et exécutent du code en dehors de SQL Server pour diverses tâches. Dans ce cas, il supprime un contexte de sécurité précédemment utilisé pour l'accès aux ressources à distance. L'API associée est en fait nommée DeleteSecurityContext ()

À ma connaissance, nous n'avons pas de ressources externes comme des serveurs liés ou des filetables. Et nous ne faisons aucune usurpation d'identité, etc. Une sauvegarde aurait-elle pu provoquer un pic ou peut-être un contrôleur de domaine défectueux?

Que diable pourrait faire que ce soit le type d'attente dominant? Comment puis-je suivre ce type d'attente plus loin?

Edit 2: J'ai vérifié le contenu du journal de sécurité Windows. Je vois quelques entrées qui peuvent être intéressantes, mais je ne sais pas si elles sont normales:

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

Edit 3 : @Jon Seigel, comme vous l'avez demandé, voici les résultats de votre requête. Un peu différent de celui de Paul:

entrez la description de l'image ici

Edit 4: Je l'avoue, je suis un premier utilisateur d'événements prolongés. J'ai ajouté ce type d'attente à l'événement wait_info_external et j'ai vu des centaines d'entrées. Il n'y a pas de texte sql ni de descripteur de plan, seulement une pile d'appels. Comment puis-je retrouver la source?

entrez la description de l'image ici


John, pouvez-vous s'il vous plaît exécuter sp_whoisactive pendant une période de temps (peut-être une minute) et voir ce qui apparaît? Cela peut vous aider / nous diriger vers une solution. sqlblog.com/files/default.aspx
Mark Wilkinson

Bonjour John, dans votre question, vous mentionnez avoir identifié SQL Server comme coupable. Pourriez-vous décrire les étapes que vous avez suivies pour arriver à cette conclusion?
Craig Efrein

Réponses:


3

Je sais que cette question, basée sur le titre, concerne principalement le type d'attente PREEMPTIVE_OS_DELETESECURITYCONTEXT, mais je pense que c'est une mauvaise direction du vrai problème qui est " un client qui se plaignait d'une utilisation élevée du processeur sur leur serveur SQL ".

La raison pour laquelle je crois que se concentrer sur ce type d'attente spécifique est une chasse aux oies sauvages, car elle augmente pour chaque connexion établie. J'exécute la requête suivante sur mon ordinateur portable (ce qui signifie que je suis le seul utilisateur):

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

Et puis je fais l'une des opérations suivantes et réexécute cette requête:

  • ouvrir un nouvel onglet de requête
  • fermez le nouvel onglet de requête
  • exécutez ce qui suit à partir d'une invite DOS: SQLCMD -E -Q "select 1"

Maintenant, nous savons que le CPU est élevé, nous devons donc regarder ce qui est en cours d'exécution pour voir quelles sessions ont un CPU élevé:

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

J'exécute généralement la requête ci-dessus telle quelle, mais vous pouvez également changer la clause ORDER BY qui est mise en commentaire pour voir si cela donne des résultats plus intéressants / utiles.

Vous pouvez également exécuter les éléments suivants, en fonction de dm_exec_query_stats, pour rechercher les requêtes les plus coûteuses. La première requête ci-dessous vous montrera des requêtes individuelles (même si elles ont plusieurs plans) et est triée par Temps CPU moyen, mais vous pouvez facilement changer cela en Lectures logiques moyennes. Une fois que vous avez trouvé une requête qui semble prendre beaucoup de ressources, copiez "sql_handle" et "statement_start_offset" dans la condition WHERE de la deuxième requête ci-dessous pour voir les plans individuels (peut être supérieur à 1). Faites défiler vers l'extrême droite et en supposant qu'il existe un plan XML, il s'affichera sous forme de lien (en mode Grille) qui vous amènera à la visionneuse de plan si vous cliquez dessus.

Requête n ° 1: obtenir des informations sur la requête

;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

Requête n ° 2: Obtenir des informations sur le plan

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

Je pensais que le type d'attente le mieux classé du script de Paul, à savoir PREEMPTIVE_OS_DELETESECURITYCONTEXT, pourrait être la cause du CPU élevé. Cela peut-il être considéré en toute sécurité comme un type d'attente bénigne dans notre cas? Dans notre application, nous avons quelques services Windows qui envoient constamment des commandes (proc stockés exécutés) à SQL Server. Je ne peux pas discerner trop de modèles de sys.dm_exec_sessions - les sessions ne restent pas ouvertes trop longtemps et il y en a beaucoup. sys.dm_exec_query_stats fournit de bonnes informations sur les processus stockés les plus chers en ce qui concerne le coût global du processeur. Cela peut être un bon point de départ.
John Russell

Je voulais juste m'assurer de ne rien manquer avec PREEMPTIVE_OS_DELETESECURITYCONTEXT. Je ne savais pas si cela pouvait être attribué à un contrôleur de domaine défectueux ou à des recherches AD?
John Russell

@JohnRussell: Je pense que le type d'attente le plus élevé est généralement un bon point de départ, mais mon point est que celui-ci n'est pas simplement déclenché par du code dans SQL Server accédant à des ressources externes, telles que Linked Server ou SQLCLR ou des processus stockés étendus (par exemple xp_dirtree), donc un volume élevé n'est pas un véritable indicateur. Et même s'il y a une latence du réseau entraînant des retards, cela augmenterait-il vraiment le processeur ou augmenterait-il simplement le blocage? Et bon point, utilisez query_stats. Je mettrai à jour ma requête avec cela.
Solomon Rutzky

1
@JohnRussell: concernant vos "services Windows qui envoient constamment des commandes", quelque chose a-t-il changé récemment? Est-ce qu'ils ferment correctement les connexions? Nettoient-ils correctement la connexion en cas d'erreur lors de la connexion? De plus, avez-vous reconstruit les index récemment ou au moins mis à jour les statistiques sur toutes les tables? Ne pas le faire pourrait entraîner une augmentation du processeur.
Solomon Rutzky

Merci pour la perspicacité! En examinant de plus près sys.dm_exec_query_stats et la fragmentation d'index sur quelques tables clés, je commence à avoir plus confiance en la cause. PREEMPTIVE_OS_DELETESECURITYCONTEXT vient de me jeter.
John Russell

1

SecurityContext est utilisé par le serveur SQL à plusieurs endroits. Un exemple que vous avez nommé est les serveurs liés et les fichiers filetables. Peut-être que vous utilisez cmdexec? Tâches de l'Agent SQL Server avec des comptes proxy? Vous appelez un webservice? Les ressources distantes peuvent être très amusantes.

Les événements d'emprunt d'identité peuvent être enregistrés dans l'événement de sécurité Windows. Il se pourrait que vous y trouviez un indice. De plus, vous voudrez peut-être vérifier l'enregistreur de la boîte noire alias les événements étendus.

Avez-vous vérifié si ces types d'attente sont nouveaux (et en relation avec le processeur élevé) ou tout simplement normaux pour votre serveur?


Nous n'avons aucun travail de l'Agent SQL Server ni WebServices. J'ai effacé les statistiques d'attente et relancé la requête d'origine ci-dessus et des statistiques similaires réapparaissent. Il m'a fallu un peu pour comprendre comment reconfigurer la session d'événement étendu system_health pour inclure wait_info_external pour waittype = 'PREEMPTIVE_OS_DELETESECURITYCONTEXT', mais je l'ai finalement ajouté, je peux voir des centaines de ces événements en quelques secondes, j'observais les données en direct . Je cherche à mieux déchiffrer la source. Avez-vous des conseils sur la façon de retracer cela?
John Russell
En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.