Recentemente, eu estava solucionando um problema de desempenho estranho que afetava o ambiente de produção de um aplicativo, mas não nenhum dos ambientes inferiores. Consegui replicar o problema em sua forma mais simples com esta consulta:
SELECT product_id, dbo.TranslateStatusToActive(status_id) FROM prod_Products
TranslateStatusToActive
é uma UDF escalar muito simples, que basicamente apenas junta o valor dado a outra tabela e retorna 1 ou 0 com base em uma case
declaração. Eu postaria o código, mas é uma função escrita pelo fornecedor e não estou particularmente interessado em ser processado hoje. (Sim, a lógica pode ser incorporada. Sim, corrige o problema de desempenho. Sim, convencemos o fornecedor a implementar a alteração. Essa não é minha pergunta.)
Ao executar em produção, a consulta levaria entre 10 e 20 segundos para retornar os resultados. Em desenvolvimento, a mesma consulta retorna em menos de 3 segundos. Os planos de execução são quase idênticos, exceto por mostrar que o tempo de CPU foi de cerca de 15.000 ms em produção e 3.000 ms em outros lugares.
Suspeitei que houvesse algumas diferenças ambientais, então configurei outro servidor que replicava as condições de produção o mais próximo possível: verifiquei o número de CPUs, a quantidade de memória fornecida ao SQL Server e o nível de patch específico (13.0.0.1). 4451) foram os mesmos.
Eu restaurei uma cópia do banco de dados de produção para este novo servidor sandbox e, para minha surpresa, a consulta foi executada tão rapidamente quanto no desenvolvimento. Mais uma vez, o plano e os dados eram idênticos, exceto pelo tempo extra de CPU. As esperas listadas no plano de execução eram do mesmo tipo e com poucos ms de distância entre si em todos os ambientes.
Sem saber o que fazer a seguir, habilitei optimize for ad hoc workloads
no servidor de produção. Isso corrigiu o problema de desempenho! Uma coisa, porém: nenhum dos outros ambientes tinha essa configuração habilitada. Eu estava limpando regularmente o procedimento e os caches do sistema em cada ambiente durante o teste, então não acho que foi o resultado de alterar uma configuração causando uma recompilação.
Perguntas
- O que poderia fazer com que a UDF fosse executada de forma tão diferente em cada ambiente, apesar do plano idêntico e dos sistemas quase idênticos?
- Por que o ambiente de produção precisava estar
optimize for ad hoc workloads
habilitado para funcionar tão bem quanto os outros ambientes, que não o habilitavam? - Existe alguma configuração que eu não pensei em verificar que pode causar uma diferença tão grande?
O desenvolvimento é compartilhado, enquanto a produção atualmente é usada apenas por este aplicativo. O uso da terceira caixa seria quase o mesmo da produção. Limpei praticamente todos os caches para os quais eles dão um DBCC
comando. O ambiente de desenvolvimento é usado regularmente como um sistema de treinamento, então estou bastante confiante de que não foi um problema de cache de plano.
A única diferença com a terceira caixa é que não há um aplicativo conectado a ela, mas houve pouco ou nenhum uso de aplicativo enquanto eu estava testando a função em produção, então a diferença foi, com base na minha experiência trabalhando nesse ambiente , insignificante. A única coisa que não pude fazer foi reiniciar o servidor de produção, mas a documentação da Microsoft afirma explicitamente que a habilitação optimize for ad hoc workloads
não limpa ou afeta nenhum plano existente, então não vejo qual seria a diferença.
A situação que você descreveu pode acontecer quando há algum tipo de monitoramento habilitado (trace, sessão de evento estendida, alguma ferramenta de terceiros), que faz algum tipo de log ou trabalho por execução de UDF (ou mesmo por instrução dentro da UDF).
Se a UDF for executada muitas vezes em uma consulta, pode haver uma sobrecarga muito grande para fazer esse monitoramento. Se o monitoramento estiver ocorrendo apenas em um servidor, você verá uma grande diferença de desempenho entre eles.
Vou tentar responder com uma analogia. Pense no SQL Server como um automóvel.
Considere a configuração "Otimizar para cargas de trabalho ad hoc" como uma caixa de engrenagens automática sofisticada. Quando ele percebe uma subida off-road, ele muda para uma marcha diferente da estrada reta. Os passageiros desfrutam de uma viagem tranquila, independentemente do terreno pelo qual o automóvel passa.
Mas como isso explica o comportamento diferente da mesma consulta em servidores semelhantes, quando a configuração não existe?
Nesse caso, as caixas de câmbio ainda são automáticas, mas não tão sofisticadas. Eles percebem e diferenciam entre terrenos, então (no automóvel A) na primeira vez que vê um terreno específico, digamos, uma subida off-road, ele ajusta uma marcha específica. O problema é que eles podem perder certos detalhes. Na próxima vez que ele vir um terreno semelhante (digamos, uma descida off-road ou uma estrada um pouco acima), ele ainda usará a mesma marcha da primeira vez. E os passageiros reclamam porque o equipamento não é o melhor.
O segundo automóvel (B) partiu em uma rota diferente, primeiro na estrada, depois fora da estrada, então a caixa de câmbio tomou decisões ligeiramente diferentes na primeira vez que encontrou terrenos semelhantes (aos de A). Felizmente as coisas correram bem e os passageiros não reclamaram. Isso pode mudar, é claro, se o terreno após o próximo turno precisar de uma marcha diferente daquela já usada para uma semelhante.
Terminologia explicada:
Mais algumas notas:
A analogia não é perfeita, é claro. Há muitos detalhes sobre como os planos são salvos e reutilizados (ou não) com a configuração "cargas de trabalho ad hoc" (ativada ou desativada).
A configuração não é um botão mágico para resolver todos os problemas. Embora seja útil em muitos casos, há motivos pelos quais o SQL Server o define como
OFF
padrão. Tenho certeza de que há muitos casos em que isso não fará nenhuma diferença ou até mesmo degradará o desempenho.Para o problema específico, não posso dizer que tenho certeza de que a descrição acima foi a causa. É apenas uma explicação possível que parece plausível (porque alterar essa configuração a corrigiu). Pode ter havido outras razões para o comportamento diferente (por exemplo, alguma outra configuração que você perdeu entre a produção e o servidor de teste) ou outros serviços/programas em execução nos dois ambientes. É praticamente impossível ter 2 configurações idênticas.
Se você puder fornecer a consulta e os planos (semelhantes), há outros usuários no site que podem esclarecer melhor o problema.
Eu verificaria se suas opções de conexão (padrão) são as mesmas em ambos os servidores, ao testar consultas que "deveriam" ser executadas de maneira semelhante: