Gérer les attentes de CXPACKET - définir un seuil de coût pour le parallélisme

12

Pour faire suite à ma question précédente sur le dépannage des performances d'un site Sharepoint , je me demandais si je pouvais faire quelque chose au sujet des attentes de CXPACKET.

Je sais que la solution instinctive consiste à désactiver tout parallélisme en définissant MAXDOP sur 1 - cela semble être une mauvaise idée. Mais une autre idée est d'augmenter le seuil de coût avant que le parallélisme ne se déclenche. La valeur par défaut de 5 pour le coût d'un plan d'exécution est assez faible.

Je me demandais donc s'il y avait une requête déjà écrite qui me trouverait les requêtes avec le coût du plan d'exécution le plus élevé (je sais que vous pouvez trouver celles avec la durée d'exécution la plus élevée et ainsi de suite - mais le coût du plan d'exécution peut-il être récupéré quelque part, aussi?) et cela me dirait également si une telle requête a été exécutée en parallèle.

Quelqu'un a-t-il un tel script à portée de main ou peut-il me diriger vers les vues de catalogue DMV, DMF ou d'autres systèmes pour le découvrir?

marc_s
la source

Réponses:

11

CXPACKETn'est jamais une cause; c'est tout le blâme, mais c'est toujours le symptôme d'autre chose. Vous devez saisir ces requêtes dans la loi et comprendre ce qu'est "autre chose". Cela peut être différent d'une requête à l'autre, et désactiver complètement le parallélisme est - comme vous l'avez suggéré - une surpuissance inutile dans la plupart des cas. Mais c'est souvent le moins de travail, c'est pourquoi il s'agit d'un «correctif» si répandu.

Si vous pouvez obtenir un plan réel pour une requête qui semble être responsable d'attentes CXPACKET élevées, chargez-le dans SQL Sentry Plan Explorer . Il y a généralement une raison derrière cela; nous montrons quelles opérations parallèles ont conduit à un biais de thread, et vous pouvez facilement corréler cela à des estimations qui sont désactivées (nous mettons en évidence les opérations avec des estimations qui sont désactivées par un seuil au moins certain). Habituellement, le problème sous-jacent est des statistiques vraiment mauvaises / obsolètes (ou non disponibles).

Malheureusement, ce que vous trouverez dans sys.dm_exec_cached_plans sont des plans estimés . Ils ne vous diront pas si le plan est parallèle lorsqu'il a été réellement utilisé, car le plan réel n'est pas ce qui est mis en cache. Dans certains cas, vous vous attendez à voir un plan série et parallèle pour la même requête; ce n'est pas ainsi que SQL Server gère la situation des plans parallèles qui pourraient être parallèles au moment de l'exécution. ( Beaucoup d'informations à ce sujet ici .)

Aaron Bertrand
la source
4

Si vous souhaitez voir le plan d'exécution réel d'une requête en cours d'exécution.

SELECT plan_handle FROM sys.dm_exec_requests WHERE session_id = [YourSPID]

Saisissez ensuite le résultat dans cette requête.

SELECT query_plan FROM sys.dm_exec_query_plan (Enter the result here.)

Cela vous montrera le plan d'exécution réel que sql a utilisé pour cette requête. Vous pouvez utiliser ce plan d'exécution pour voir sur quel thread vous attendez.

J'ai également constaté que la désactivation de l'hyper threading réduisait considérablement mes temps d'attente CXpacket.

J'espère que cela pourra aider.

Zane
la source
3

La réponse ci-dessus d'Aaron est correcte.

J'aimerais simplement ajouter que, si vous n'utilisez pas déjà SQL Performance Dashboard Reports et le collecteur de données intégré , vous devriez commencer.

Vous pouvez également prendre la requête suivante et la modifier comme bon vous semble:

DECLARE @MinExecutions int; 
SET @MinExecutions = 5 

SELECT EQS.total_worker_time AS TotalWorkerTime 
      ,EQS.total_logical_reads + EQS.total_logical_writes AS TotalLogicalIO 
      ,EQS.execution_count As ExeCnt 
      ,EQS.last_execution_time AS LastUsage 
      ,EQS.total_worker_time / EQS.execution_count as AvgCPUTimeMiS 
      ,(EQS.total_logical_reads + EQS.total_logical_writes) / EQS.execution_count  
       AS AvgLogicalIO 
      ,DB.name AS DatabaseName 
      ,SUBSTRING(EST.text 
                ,1 + EQS.statement_start_offset / 2 
                ,(CASE WHEN EQS.statement_end_offset = -1  
                       THEN LEN(convert(nvarchar(max), EST.text)) * 2  
                       ELSE EQS.statement_end_offset END  
                 - EQS.statement_start_offset) / 2 
                ) AS SqlStatement 
      -- Optional with Query plan; remove comment to show, but then the query takes !!much longer!! 
      --,EQP.[query_plan] AS [QueryPlan] 
FROM sys.dm_exec_query_stats AS EQS 
     CROSS APPLY sys.dm_exec_sql_text(EQS.sql_handle) AS EST 
     CROSS APPLY sys.dm_exec_query_plan(EQS.plan_handle) AS EQP 
     LEFT JOIN sys.databases AS DB 
         ON EST.dbid = DB.database_id      
WHERE EQS.execution_count > @MinExecutions 
      AND EQS.last_execution_time > DATEDIFF(MONTH, -1, GETDATE()) 
ORDER BY AvgLogicalIo DESC 
        ,AvgCPUTimeMiS DESC
mardi
la source
0

Dans mon expérience précédente, le seuil de coût pour le parallélisme n'a pas aidé à réduire CXPACKET.

Une CXPACKETattente élevée peut se produire en raison de statistiques incorrectes entraînant un parallélisme asymétrique.

  1. Plus d'informations sur CXPACKET Waits: Skewed Parallelism
  2. Élément Microsoft Connect
  3. Ma requête est (pas) en attente à cause du parallélisme? - Tim Ford

Voici SQL que j'ai utilisé pour trouver des sessions contenant à la fois CXPacket et " autres attentes " (veuillez consulter le schéma ci-dessous).

SQL

DECLARE @RawResult TABLE ([database_id] INT,[session_id] INT,exec_context_id INT, [blocking_session_id] INT,task_state VARCHAR(20),
                          [cpu_time] BIGINT,[wait_duration_ms] BIGINT, [wait_type] VARCHAR(100),[resource_description] nvarchar(3072),
                          [sql_handle] varbinary(64),[plan_handle] varbinary(64)
                          )
INSERT INTO @RawResult
SELECT 
    [R].[database_id],
    [S].[session_id],
    [W].exec_context_id,
    [W].blocking_session_id,
    [T].task_state,
    [R].[cpu_time],
    [W].[wait_duration_ms],
    [W].[wait_type],
    [W].[resource_description],
    [R].[sql_handle],
    [R].[plan_handle]
FROM sys.dm_os_waiting_tasks [W]
INNER JOIN sys.dm_os_tasks [T] ON
    [W].[waiting_task_address] = [T].[task_address]
INNER JOIN sys.dm_exec_sessions [S] ON
    [W].[session_id] = [S].[session_id]
INNER JOIN sys.dm_exec_requests [R] ON
    [S].[session_id] = [R].[session_id]
WHERE [S].[is_user_process] = 1
--AND S.session_id <> @@SPID--???
--ORDER BY [W].[session_id],[W].[exec_context_id];


SELECT  
    DB_NAME(C.database_id) AS database_name,
    C.[database_id],
    C.[session_id],
    C.exec_context_id,
    C.blocking_session_id,
    C.task_state,
    C.[cpu_time],
    C.[wait_duration_ms],
    C.[wait_type],
    C.[sql_handle],
    C.[plan_handle],
    [H].text,
    [P].[query_plan],
    C.[resource_description]
FROM @RawResult C
OUTER APPLY sys.dm_exec_sql_text (C.[sql_handle]) [H]
OUTER APPLY sys.dm_exec_query_plan (C.[plan_handle]) [P]
WHERE C.[session_id] IN
                    (
                        SELECT A.[session_id]
                        FROM @RawResult A
                        INNER JOIN @RawResult B
                            ON A.[session_id] = B.[session_id]
                            AND A.wait_type='CXPACKET'
                            AND B.wait_type <> 'CXPACKET'
                    )
ORDER BY C.[session_id],C.[exec_context_id]

entrez la description de l'image ici

Les analyses volumineuses peuvent également faire partie de la cause première. Lorsque j'ai vérifié le plan d'exécution de la requête ci-dessus, j'ai trouvé un tel scan dans ma base de données. Il y avait également une suggestion d'index manquante dans le plan d'exécution.

entrez la description de l'image ici


LCJ
la source