Lidando com as esperas do CXPACKET - definindo o limite de custo para o paralelismo

12

Como acompanhamento da minha pergunta anterior sobre como solucionar problemas de um site do Sharepoint , fiquei pensando se poderia fazer algo sobre as esperas do CXPACKET.

Eu sei que a solução definitiva é desativar todo o paralelismo, definindo MAXDOP como 1 - parece uma má idéia. Mas outra idéia é aumentar o limite de custo antes que o paralelismo comece. O padrão de 5 para o custo de um plano de execução é bastante baixo.

Então, eu queria saber se já existe uma consulta escrita que me encontraria com o custo mais alto do plano de execução (eu sei que você pode encontrar aquelas com a maior duração de execução e assim por diante - mas o custo do plano de execução pode ser recuperado em algum lugar, também?) e isso também me diria se essa consulta foi executada em paralelo.

Alguém tem esse script em mãos ou pode me indicar a direção da DMV, DMF ou outras visualizações relevantes do catálogo do sistema para descobrir isso?

marc_s
fonte

Respostas:

11

CXPACKETnunca é uma causa; recebe toda a culpa, mas é sempre um sintoma de outra coisa. Você precisa capturar essas consultas no ato e descobrir o que é "outra coisa". Pode ser diferente de consulta para consulta, e desativar o paralelismo por completo é - como você sugeriu - um exagero desnecessário na maioria dos casos. Mas geralmente é a menor quantidade de trabalho, e é por isso que é uma "correção" tão predominante.

Se você puder obter um plano real para uma consulta que pareça ser responsável por altas esperas de CXPACKET, carregue-a no SQL Sentry Plan Explorer . Geralmente, há uma razão por trás disso; mostramos quais operações paralelas levaram à distorção do encadeamento e é possível correlacioná-lo facilmente com estimativas desativadas (destacamos operações com estimativas desativadas por pelo menos um limite). Normalmente, o problema subjacente é realmente ruim / estatísticas desatualizadas (ou indisponíveis).

Infelizmente, o que você encontrará em sys.dm_exec_cached_plans são planos estimados . Eles não dirão se o plano ficou paralelo quando foi realmente usado, porque o plano real não é o que é armazenado em cache. Em alguns casos, você espera ver um plano serial e paralelo para a mesma consulta; não é assim que o SQL Server lida com a situação dos planos paralelos que podem ser paralelos no tempo de execução. ( Muitas informações sobre isso aqui .)

Aaron Bertrand
fonte
4

Se você deseja ver o plano de execução real de uma consulta em execução.

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

Primeiro, em seguida, insira o resultado nesta consulta.

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

Isso mostrará o plano de execução real que o sql usou para essa consulta. Você pode usar esse plano de execução para ver em qual thread está esperando.

Também descobri que desligar o hyper threading reduziu drasticamente meus tempos de espera do CXpacket.

Espero que ajude.

Zane
fonte
3

A resposta acima de Aaron está correta.

Gostaria de acrescentar que, se você ainda não estiver usando o SQL Performance Dashboard Reports e o Data Collector interno , você deve iniciar.

Você também pode fazer a seguinte consulta e modificá-la como achar melhor:

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
terça-feira
fonte
0

Na minha experiência anterior, o Cost Threshold For Parallelism não ajudou a reduzir o CXPACKET.

Uma CXPACKETespera alta pode ocorrer devido a estatísticas incorretas, resultando em paralelismo distorcido.

  1. Mais sobre CXPACKET Waits: Parallelism enviesado
  2. Item do Microsoft Connect
  3. Minha consulta está (não) aguardando por causa do paralelismo? - Tim Ford

A seguir, o SQL, que eu costumava encontrar sessões com CXPacket e " outras esperas " (veja o dagrama abaixo).

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]

insira a descrição da imagem aqui

Grandes Scans também pode ser parte da causa raiz. Quando verifiquei o plano de execução da consulta acima, encontrei uma dessas verificações no meu banco de dados. Também havia uma sugestão de índice ausente no plano de execução.

insira a descrição da imagem aqui


LCJ
fonte