Instruções do SQL Server lentas intermitentemente no SQL Server 2008 R2

13

Em um de nossos clientes, estamos enfrentando alguns problemas de desempenho em nosso aplicativo. É um aplicativo Web .NET 3.5 que está consumindo e atualizando dados em um banco de dados do SQL Server. Atualmente, nosso ambiente de produção consiste em um computador com Windows 2008 R2 como front-end e um cluster do SQL Server 2008 R2 no back-end. Nosso aplicativo usa COM + e MSDTC para se conectar ao banco de dados.

Aqui está o que está acontecendo: nossos usuários finais às vezes reclamam de lentidão no aplicativo. Algumas páginas levam mais tempo para carregar do que seria esperado. Enquanto tentava descobrir o que estava acontecendo, consegui descobrir algum comportamento estranho no lado do banco de dados que pode ser a causa da degradação do desempenho. Notei que, às vezes, existem algumas instruções SQL que levam muito mais tempo para serem executadas do que seria esperado. Consegui identificar algumas dessas instruções (principalmente são invocações de alguns dos procedimentos armazenados de nosso aplicativo) usando um rastreamento de criador de perfil (com o modelo TSQL_Duration) para identificar as consultas de execução longa.

O problema é que, quando eu executo esses procedimentos armazenados diretamente no banco de dados no SQL Management Studio, às vezes eles demoram muito (cerca de 7/8 segundos), outras vezes, são rápidos (menos de 1 segundo). Não sei por que isso acontece e está me deixando louco, porque a máquina SQL (4 núcleos, 32 GB) não está sendo usada por nenhum outro aplicativo e essas consultas não devem demorar muito para serem executadas.

Não sendo um DBA ou um guru do SQL Server, tenho tentado analisar algumas coisas que podem me ajudar a entender o problema. Aqui estão as etapas que eu tomei para tentar resolver o problema e o que descobri até agora:

  • Todo o código TSQL chamado pelo aplicativo é gravado em procedimentos armazenados.
  • Eu identifiquei algumas das consultas de longa execução no SQL Server Profiler, no entanto, quando as executo no Management Studio, elas demoram muito para serem executadas (de 4 a 10 segundos) ou são executadas rapidamente (menos de 1 segundo). Estou executando exatamente as mesmas consultas com os mesmos dados passados ​​nos parâmetros. Essas consultas são principalmente procedimentos armazenados com instruções de seleção neles.
  • Tentei examinar as estatísticas de espera e filas para tentar descobrir se existem processos aguardando alguns recursos. Eu executei a seguinte consulta:

WITH Waits AS
    (SELECT
        wait_type,
        wait_time_ms / 1000.0 AS WaitS,
        (wait_time_ms - signal_wait_time_ms) / 1000.0 AS ResourceS,
        signal_wait_time_ms / 1000.0 AS SignalS,
        waiting_tasks_count AS WaitCount,
        100.0 * wait_time_ms / SUM (wait_time_ms) OVER() AS Percentage,
        ROW_NUMBER() OVER(ORDER BY wait_time_ms DESC) AS RowNum
    FROM sys.dm_os_wait_stats
    WHERE wait_type NOT IN (
        'CLR_SEMAPHORE', 'LAZYWRITER_SLEEP', 'RESOURCE_QUEUE', 'SLEEP_TASK',
        'SLEEP_SYSTEMTASK', 'SQLTRACE_BUFFER_FLUSH', 'WAITFOR', 'LOGMGR_QUEUE',
        'CHECKPOINT_QUEUE', 'REQUEST_FOR_DEADLOCK_SEARCH', 'XE_TIMER_EVENT',  'BROKER_TO_FLUSH',
        'BROKER_TASK_STOP', 'CLR_MANUAL_EVENT', 'CLR_AUTO_EVENT',     'DISPATCHER_QUEUE_SEMAPHORE',
        'FT_IFTS_SCHEDULER_IDLE_WAIT', 'XE_DISPATCHER_WAIT', 'XE_DISPATCHER_JOIN', 'BROKER_EVENTHANDLER',
        'TRACEWRITE', 'FT_IFTSHC_MUTEX', 'SQLTRACE_INCREMENTAL_FLUSH_SLEEP',
        'BROKER_RECEIVE_WAITFOR', 'ONDEMAND_TASK_QUEUE', 'DBMIRROR_EVENTS_QUEUE',
        'DBMIRRORING_CMD', 'BROKER_TRANSMITTER', 'SQLTRACE_WAIT_ENTRIES',
        'SLEEP_BPOOL_FLUSH', 'SQLTRACE_LOCK')
    )
SELECT
    W1.wait_type AS WaitType, 
    CAST (W1.WaitS AS DECIMAL(14, 2)) AS Wait_S,
    CAST (W1.ResourceS AS DECIMAL(14, 2)) AS Resource_S,
    CAST (W1.SignalS AS DECIMAL(14, 2)) AS Signal_S,
    W1.WaitCount AS WaitCount,
    CAST (W1.Percentage AS DECIMAL(4, 2)) AS Percentage,
    CAST ((W1.WaitS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgWait_S,
    CAST ((W1.ResourceS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgRes_S,
    CAST ((W1.SignalS / W1.WaitCount) AS DECIMAL (14, 4)) AS AvgSig_S
FROM Waits AS W1
    INNER JOIN Waits AS W2 ON W2.RowNum <= W1.RowNum
GROUP BY W1.RowNum, W1.wait_type, W1.WaitS, W1.ResourceS, W1.SignalS, W1.WaitCount,    W1.Percentage
HAVING SUM (W2.Percentage) - W1.Percentage < 95; -- percentage threshold
GO

Aqui está o que eu descobri:

  • Depois de redefinir as estatísticas usando DBCC SQLPERF (cerca de 1 ou 2 horas depois), os tipos de espera que tenho mais são SOS_SCHEDULER_YIELD e WRITELOG
  • Com o tempo (após aproximadamente 1 dia de execução), os tipos de espera que mais ocorrem no banco de dados são CXPACKET (67%) e OLEDB (17%), mesmo que o tempo médio de espera para cada um não seja longo. Também notei que as instruções de execução mais longa identificadas no SQL Profiler são chamadas para procedimentos armazenados que retornam mais de um conjunto de resultados (geralmente 3). Pode haver um problema de paralelismo aqui? Existe alguma maneira de tentar identificar se essa é a causa do problema?
  • Li em algum lugar que o OLEDB espera pode ser causado por chamadas para recursos do OLEDB, como servidores vinculados. Temos um servidor vinculado para conectar-se a uma máquina dos Serviços de Indexação (MSIDXS), no entanto, nenhuma das instruções identificadas há muito tempo faz uso desse servidor vinculado.
  • O tempo médio de espera mais alto que eu tenho é para esperas do tipo LCK_M_X (média de 1,5 segundos), mas esses tipos de espera não ocorrem com muita frequência em comparação com outros tipos (por exemplo, 64 LCK_M_X aguarda vs 10,823 CXPACKET aguarda no mesmo período de tempo )
  • Uma coisa que notei é que o serviço MSDTC não está em cluster. O serviço SQL Server está em cluster, mas não o MSDTC. Pode haver um impacto no desempenho por causa disso? Estamos usando o MSDTC porque nosso aplicativo usa os Serviços Corporativos (DCOM) para acessar o banco de dados, mas os servidores não foram instalados e configurados por nós, mas por nosso cliente.

Alguém pode me ajudar a entender melhor esses dados? Alguém pode me ajudar a entender o que pode estar acontecendo? Existe algo que eu possa fazer no servidor para tentar descobrir as coisas? Devo falar com a equipe de desenvolvimento de aplicativos?

Dori
fonte

Respostas:

4

Obrigado pela explicação detalhada do seu problema (na verdade, uma das melhores perguntas apresentadas).

WRITELOG é um tipo de espera muito comum, portanto, não se preocupe. Observando o SOS_SCHEDULER_YIELD indica a pressão da CPU e também o CXPACKET, é possível que haja alguns índices ausentes e você esteja recuperando muitos dados das consultas para um sistema OLTP. Sugiro que você analise a DMV de índices ausentes e verifique se há algum índice (quase certo de que haverá mais do que poucos) que esteja nos procs questionáveis.

http://sqlfool.com/2009/04/a-look-at-missing-indexes/

http://troubleshootingsql.com/2009/12/30/how-to-find-out-the-missing-indexes-on-a-sql-server-2008-or-2005-instance-along-with-the- comandos create-index-/

Procure também a publicação de Jonathan Kehayias no sqlblog.com.

Além disso, dê uma olhada no Parameter sniffing.

http://sommarskog.se/query-plan-mysteries.html

http://pratchev.blogspot.com/2007/08/parameter-sniffing.html

NÃO é uma resposta competitiva para as suas necessidades, mas um bom ponto de partida. Deixe-nos saber se você precisar de mais detalhes.

Sankar Reddy
fonte
1

Tivemos um problema semelhante depois que um dos funcionários reescreveu alguns dos procedimentos armazenados. Aconteceu que havia ramificações excessivas e o SQL Dinâmico sendo construído, alterando significativamente a cláusula where.

Por exemplo (simplificado, é claro):

Se o Model for "X", a cláusula where procurou por ProductCode igual a certos valores.
Se o Modelo for "Y", a cláusula where procurou por ProductType igual a certos valores.

O SQL Server criará um plano de consulta com base nos parâmetros de entrada na primeira vez que o procedimento armazenado for executado. Portanto, se o plano de consulta é baseado na lógica que usa "ProductCode" é igual e você está solicitando "ProductType" é igual a um plano de consulta incompatível e provavelmente resulta em uma verificação completa da tabela.

Você pode tentar colocar " WITH RECOMPILE " na parte superior do procedimento armazenado. CRIAR PROCEDIMENTO (Transact-SQL)

A melhor maneira de descrever isso é a seguinte:

Suponha que você tenha uma lista de nomes e números de telefone classificados por Sobrenome. Isso funciona muito bem para encontrar pessoas usando seu Sobrenome (plano de consulta com base no Sobrenome). Agora, suponha que você precise de todos os nomes e números de telefone no Código de Área 203. Se sua lista estiver classificada por Sobrenome, a única maneira de obter uma lista completa de todas as pessoas do Código de Área 203 é começar do topo e ler sequencialmente cada uma delas e todo registro. (Verificação completa da tabela).

Michael Riley - também conhecido por Gunny
fonte
Usar a exec()função explicaria o comportamento observado. Nesse caso, o uso sp_executesqlnormalmente resolve os problemas com instruções SQL dinâmicas.
ajeh
1

Se as consultas estiverem sendo executadas de forma intermitente rápida e lenta no SSMS e no aplicativo, você poderá ter um problema de sniffing de estatísticas ou parâmetros.

Eu executaria esses procedimentos armazenados e, em seguida, revisaria o plano de execução para obter as propriedades do operador raiz (nó verde na extrema esquerda de cada instrução).

Qual é o número estimado de linhas no plano de execução, versus quantas linhas reais foram retornadas?

O parâmetro compilado corresponde ao parâmetro de consulta real?

Se o plano de execução foi criado para um parâmetro que retorna apenas um punhado de linhas e você executa o mesmo procedimento com um parâmetro que retorna um grande número de linhas, o SQL pode usar o plano de execução incorreto para a consulta.

As opções do plano de execução estão intimamente vinculadas às estatísticas SQL, portanto, é uma boa ideia reconstruir suas estatísticas regularmente.

Se você possui um procedimento armazenado que, às vezes, retorna pequenas quantidades de dados ou grandes quantidades de dados, dependendo do parâmetro fornecido, você pode ter um problema de detecção de parâmetros.

Se a reconstrução de suas estatísticas não resolver o problema, você poderá executar as instruções mais caras no procedimento armazenado com OPTION (RECOMPILE)

Andre Ranieri
fonte
0

Como você identificou consultas de longa execução, é possível buscar os planos de execução desses procedimentos no cache e verificar se é possível determinar o problema. Geralmente, há conversões implícitas ou em tempo de execução dos tipos de dados. Além disso, se você limpar ou inserir muitos dados, também é recomendável atualizar as estatísticas.

Chandan jha
fonte