Plano de execução repentinamente lento para proc armazenado

15

Estou tentando entender um problema que estamos enfrentando no SQL Server 2000. Somos um site moderadamente transacional e temos um processo armazenado chamado sp_GetCurrentTransactionsque aceita um ID do cliente e duas datas.

Agora, dependendo das datas e do cliente, essa consulta pode retornar de zero a 1000s de linhas.

O problema: o que experimentamos é que, de repente, obteremos vários erros (normalmente Execution Timeout Expiredou similares) para um cliente em particular enquanto eles tentam executar o processo armazenado. Então, examinamos a consulta, executamos no SSMS e descobrimos que são necessários 30s. Por isso, recompilamos o proc armazenado e o -bang- é executado agora em 300ms.

Eu falei com o nosso DBA sobre isso. Ele me disse que o banco de dados criou um plano de consulta quando criamos o processo armazenado. Ele disse que era um bom plano para esse conjunto de parâmetros, mas se você lançar um determinado conjunto de parâmetros, o plano não será o melhor plano para esses dados e, portanto, você o verá lento.

As opções apresentadas são: mover essa consulta de problema de um processo armazenado e voltar ao SQL dinâmico, com seu plano de execução criado a cada execução.

Isso parece um passo para mim e sinto que deve haver uma maneira de contornar isso. Existe alguma outra maneira de lidar com esse problema?

Toda e qualquer resposta é apreciada.

Arqueiro Ciaran
fonte
existe uma declaração if / else no proc? Vi isso acontecer quando o plano é armazenado em cache na instrução if e, em seguida, tenta executar no bloco else usando o plano errado. Esses erros corresponderam a uma alteração no processo?
Jeremy Gray
@ Jeremy: Nenhuma alteração no proc e nenhuma declaração else / if.
Ciaran Archer

Respostas:

14

Esse problema é chamado de sniffing de parâmetro.

Versões posteriores do SQL Server oferecem mais opções para lidar com isso, como dicas OPTION (RECOMPILE)ou OPTIMIZE FORsugestões.

Você pode tentar declarar variáveis ​​no procedimento armazenado, atribuir os valores dos parâmetros às variáveis ​​e usá-las no lugar dos parâmetros, pois parece que na maioria das vezes você está obtendo um plano razoavelmente satisfatório.

Normalmente, os planos mais catastroficamente ruins são aqueles compilados para parâmetros com seletividade muito alta, mas executados com parâmetros com baixa seletividade.

Supondo que o plano gerado seja mais robusto com essa abordagem e satisfatório para todos os valores de parâmetros, a vantagem dessa abordagem sobre a sugerida pelo JNK é que ele não incorre em um custo de compilação para cada chamada.

A desvantagem é que, para algumas execuções, o tempo de execução pode ser maior do que para um plano personalizado especificamente para esses valores de parâmetro, portanto, é uma compensação entre tempo de compilação e tempo de execução.

Martin Smith
fonte
3
Ou "vincular espreitar" na terminologia Oracle
Gaius
Graças @Gaius, é bom saber terminologia para mais de um RDBMS;)
Andrei Rînea
6

Em vez de usar SQL dinâmico, você sempre pode alterar suas chamadas de proc para:

EXEC Database.dbo.usp_Myprocedure 'Parameter' WITH RECOMPILE

As WITH RECOMPILEforças (você adivinhou!) Recompilam o plano de execução sempre que ele é executado.

Você também pode incluir WITH RECOMPILEna definição do processo armazenado:

CREATE PROCEDURE usp.MyProcedure (Parameters)
WITH RECOMPILE
AS
...
JNK
fonte
2

Você também pode tentar decidir o banco de dados que planeja usar, embora esteja lutando um pouco com o otimizador, por isso é mais frágil do que você esperaria.

A técnica é esta - divida o procedimento armazenado em 2, um destinado a um conjunto de parâmetros, um a outro. Adicione cláusulas where a cada uma delas para que, entre elas, abranjam todos os casos possíveis. Observe os planos de consulta - um deve ser otimizado para um conjunto de parâmetros, o outro para o outro conjunto. Talvez seja necessário mexer na consulta para que isso aconteça, ou talvez isso não seja possível para a sua consulta; nesse caso, essa abordagem não funcionará.

Agora faça seu procedimento armazenado original verificar os valores dos parâmetros e despachar para o apropriado dos dois procedimentos armazenados do parágrafo anterior.

Isso pode funcionar, mas é uma espécie de invasão para forçar o otimizador a funcionar com mais eficiência na sua consulta. Como todos os hacks, em versões futuras do banco de dados, pode ser desnecessário ou até piorar as coisas. Portanto, mesmo que funcione, você deve decidir se vale a pena.

psr
fonte
1

Você também pode tentar SET FORCEPLANe indexar dicas.

http://msdn.microsoft.com/en-us/library/ms188344.aspx
Basicamente, você pode escolher em que ordem a associação ocorre.

Você pode ter dicas de índice para garantir que o SQL Server use os índices corretos.

user606723
fonte
0

Hmmm ... se estivermos focados apenas nesse procedimento armazenado, ficaria surpreso que o uso do plano de execução em cache estivesse causando o problema que você está vendo. Eu pediria para ver o plano de execução do procedimento armazenado usando um conjunto de parâmetros para o cliente e as duas datas. Gostaria de saber se um índice mais específico seria útil -> como em customerId, e apenas as duas datas?

Brian Knight
fonte
2
Por que a surpresa? sniffing de parâmetro é um problema bastante comum com esses sintomas e parece que o DBA identificou isso como o problema.
Martin Smith
@MartinSmith - Estou surpreso um pouco que o DBA, que sabe sobre cheirar os parametros, não saiba sobre dicas de recompilação ...
JNK /
@JNK - Isso é verdade. Não sei por que eles não mencionaram isso.
Martin Smith
0

O desempenho subitamente degradante parece um plano de consulta ineficiente produzido, provavelmente como resultado de estatísticas ausentes. Execute um criador de perfil do SQL Server com as categorias de evento "Erros e avisos" definidos e verifique se há avisos sobre estatísticas ausentes.

Você também pode estar perdendo um índice ou pode precisar desfragmentar os índices, pois eles podem ser fragmentados demais para o SQL Server usar, resultando no pensamento de que uma varredura de tabela produzirá menos E / S.

O @JNK levanta uma grande questão sobre os procs armazenados - eles são compilados antecipadamente e o plano de consulta será armazenado junto com o procedimento armazenado.

Não concordo necessariamente com o uso de WITH RECOMPILE, pois você perde o benefício do armazenamento e reutilização do plano de consulta. Existem alguns casos em que isso é necessário - ou seja, se suas estatísticas de distribuição nas tabelas subjacentes diferem muito entre as chamadas, mas geralmente, uma vez que os dados nas tabelas estão maduros, a distribuição dos dados nas tabelas varia minimamente.

Então, para resumir:

  1. Verifique se há estatísticas ausentes
  2. Verifique a fragmentação do índice
  3. Crie e use um processo armazenado
  4. Renomeie o proc - sp_ é um espaço de nome de prefixo suavemente reservado para os procs do SQL Server do sistema interno - resultando no SQL Server sempre procurando no banco de dados mestre os procedimentos armazenados primeiro. Renomear o proc usp_ em vez de sp_ resultará em um aumento de desempenho, mas duvido que seja seu problema neste caso.
ifx
fonte