Estou tentando entender um problema que estamos enfrentando no SQL Server 2000. Somos um site moderadamente transacional e temos um processo armazenado chamado sp_GetCurrentTransactions
que 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 Expired
ou 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.
fonte
Respostas:
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)
ouOPTIMIZE FOR
sugestõ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.
fonte
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 RECOMPILE
forças (você adivinhou!) Recompilam o plano de execução sempre que ele é executado.Você também pode incluir
WITH RECOMPILE
na definição do processo armazenado:fonte
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.
fonte
Você também pode tentar
SET FORCEPLAN
e 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.
fonte
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?
fonte
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:
fonte