Eu tenho um caso típico em que o sniffing de parâmetros faz com que um plano de execução "ruim" chegue ao cache do plano, fazendo com que as execuções subseqüentes do meu procedimento armazenado sejam muito lentas. Eu posso "resolver" esse problema com variáveis locais,, OPTIMIZE FOR ... UNKNOWN
e OPTION(RECOMPILE)
. No entanto, também posso mergulhar na consulta e tentar otimizá-la.
Estou tentando determinar se devo : com tempo limitado para corrigir problemas, gostaria de saber o custo de não fazer isso. A meu ver, se eu continuar OPTION(RECOMPILE)
, o efeito líquido é que um plano de consulta é recriado toda vez que a consulta é executada. Então, acho que preciso saber:
Como descobrir qual é o custo da criação de um plano de consulta?
Para responder minha própria pergunta, pesquisei no Google (por exemplo, com esta consulta ) e examinei a documentação das colunas da dm_exec_query_stats
DMV . Também examinei a janela de saída no SSMS para "Plano de consulta real" para encontrar essas informações. Finalmente, eu tenho procurado DBA.SE . Nenhum deles levou a uma resposta.
Alguém pode me dizer? É possível encontrar ou medir o tempo necessário para a criação do plano?
fonte
Respostas:
Você pode examinar as propriedades do nó raiz no plano de consulta, por exemplo:
(captura de tela do Sentry One Plan Explorer gratuito )
Essas informações também estão disponíveis consultando o cache do plano, por exemplo, usando uma consulta baseada nos seguintes relacionamentos:
Para um tratamento completo das opções que você tem para lidar com esse tipo de consulta, consulte o artigo recentemente atualizado de Erland Sommarskog .
fonte
Assumindo que "custo" é em termos de tempo (embora não tenha certeza do que mais poderia ser em termos de ;-), pelo menos você deve ter uma noção disso fazendo algo como o seguinte:
O primeiro item relatado na guia "Mensagens" deve ser:
Eu executaria isso pelo menos 10 vezes e calcularia a média dos milissegundos "CPU" e "Elapsed".
Idealmente, você executaria isso no Production para obter uma estimativa de tempo real, mas raramente as pessoas têm permissão para limpar o cache do plano no Production. Felizmente, a partir do SQL Server 2008, foi possível limpar um plano específico do cache. Nesse caso, você pode fazer o seguinte:
No entanto, dependendo da variabilidade dos valores transmitidos para o (s) parâmetro (s) que causam o plano em cache "ruim", há outro método a considerar que é o meio termo entre
OPTION(RECOMPILE)
eOPTION(OPTIMIZE FOR UNKNOWN)
: Dynamic SQL. Sim, eu disse. E eu até quero dizer SQL dinâmico não parametrizado. Aqui está o porquê.Você tem claramente dados que têm uma distribuição desigual, pelo menos em termos de um ou mais valores de parâmetros de entrada. As desvantagens das opções mencionadas são:
OPTION(RECOMPILE)
irá gerar um plano para cada execução e você nunca poderá se beneficiar de qualquer reutilização do plano, mesmo que os valores dos parâmetros passados novamente sejam idênticos às execuções anteriores. Para procs que são chamados com freqüência - uma vez a cada poucos segundos ou mais frequentemente - isso o salvará de uma situação horrível ocasional, mas ainda o deixará em uma situação sempre não tão boa.OPTION(OPTIMIZE FOR (@Param = value))
irá gerar um plano com base nesse valor específico, o que pode ajudar vários casos, mas ainda deixa você aberto ao problema atual.OPTION(OPTIMIZE FOR UNKNOWN)
gerará um plano com base no que equivale a uma distribuição média, o que ajudará algumas consultas, mas prejudicará outras. Deve ser o mesmo que a opção de usar variáveis locais.O SQL dinâmico, no entanto, quando feito corretamente , permitirá que os vários valores passados tenham seus próprios planos de consulta separados, ideais (bem, tanto quanto serão). O principal custo aqui é que, à medida que a variedade de valores transmitidos aumenta, o número de planos de execução no cache aumenta e eles consomem memória. Os custos menores são:
Necessidade de Validar Parâmetros de Cadeia de Caracteres para Evitar Injeções de SQL
possivelmente precisando configurar um certificado e um usuário baseado em certificado para manter a abstração de segurança ideal, pois o SQL dinâmico requer permissões diretas de tabela.
Então, aqui está como eu gerenciei essa situação quando tive procs chamados mais de uma vez por segundo e atingindo várias tabelas, cada uma com milhões de linhas. Eu tentei,
OPTION(RECOMPILE)
mas isso provou ser muito prejudicial para o processo nos 99% dos casos que não tinham o parâmetro sniffing / bad cache plan problem. E lembre-se de que um desses procs tinha cerca de 15 consultas e apenas 3 - 5 delas foram convertidas em Dynamic SQL, conforme descrito aqui; O SQL dinâmico não foi usado, a menos que fosse necessário para uma consulta específica.Se houver vários parâmetros de entrada para o procedimento armazenado, descubra quais são usados com colunas com distribuições de dados altamente díspares (e, portanto, causam esse problema) e quais são usados com colunas com distribuições mais uniformes (e não devem ser causando esse problema).
Crie a cadeia de caracteres Dynamic SQL usando parâmetros para os parâmetros de entrada proc associados a colunas distribuídas uniformemente. Essa parametrização ajuda a reduzir o aumento resultante nos planos de execução no cache relacionado a esta consulta.
Para os parâmetros restantes associados a distribuições altamente variadas, esses devem ser concatenados no SQL dinâmico como valores literais. Como uma consulta exclusiva é determinada por quaisquer alterações no texto da consulta, ter
WHERE StatusID = 1
é uma consulta diferente e, portanto, um plano de consulta diferente do que terWHERE StatusID = 2
.Se algum dos parâmetros de entrada proc que devem ser concatenados no texto da consulta forem cadeias, eles deverão ser validados para se proteger contra a injeção SQL (embora isso seja menos provável de acontecer se as cadeias transmitidas forem geradas pelo aplicativo e não um usuário, mas ainda). Faça pelo menos isso
REPLACE(@Param, '''', '''''')
para garantir que as aspas simples se tornem aspas simples.Se necessário, crie um certificado que será usado para criar um usuário e assine o procedimento armazenado, de modo que as permissões diretas da tabela sejam concedidas apenas ao novo usuário baseado em certificado e não para
[public]
ou para usuários que não deveriam ter essas permissões. .Exemplo proc:
fonte
OPTION
no meu query), e não iria me machucar muito muito desde esta sproc é bem aproveitado em testes de integração. - De qualquer forma: obrigado por suas idéias!