Statisticas e Plano de Execução
Semana passada me deparei com um problema de performance de uma procedure chamada pela minha aplicação Win32 exibindo uma mensagem de TimeOut para o usuário. Somente após as estatísticas das tabelas serem atualizadas é que o processo conseguiu rodar.
Vamos analisar um pouco o problema.
Primeira pergunta: O que são e para que servem as estatísticas das tabelas?
O SQL Server guarda informações em relação aos registros das tabelas para que o Otimizador possa decidir qual o melhor plano de execução para uma determinada consulta baseado nos dados que ele lê nas statistics das tabelas.
Vamos a um exemplo:
MASTER
database INSIDE
database INSIDE set AUTO_UPDATE_STATISTICS off
INSIDE
table teste
table teste (id Int Identity(1,1), Nome VarChar(200))
nocount on
@i Int
@i = 0
@i < 1000
@i < 10000
index ix_teste on teste(nome)
SHOW_Statistics(teste, ix_teste)
* from teste
nome = ‘A’
@i Int
@i = 0
@i < 10000
* from teste
nome = ‘X’
STATISTICS teste
* from teste
nome = ‘X’
Segunda pergunta: Como identificar o problema?
Identifiquei que o problema era um mal plano de execução gerado pelo SQL de uma consulta feita dentro da procedure que retornava os dados do processo.
Esta consulta fazia vários joins com tabelas com mais de 15 milhões de registros e o SQL Server estava fazendo um Index Scan em quase todas estas tabelas.
Rodei a procedure no SSMS com a opção para exibir o Plano de Execução (CTRL+M) ligada e tive o seguinte resultado.
Observando a imagem acima podemos identificar que as querys 5 e 6 são responsáveis pelo gargalo da procedure então me limitei em analisar estas consultas.
Após analisar as consultas constatei que o SQL Server não estava gerando um bom plano de execução para as consultas já que ele estava gerando vários Index Scan ao invés de Seeks.
Terceira pergunta: Como resolver o problema?
Após identificar que o problema era o plano de execução temos várias maneiras de forçar o SQL Server a gera determinado plano por exemplo com o uso de Hints, no meu caso achei melhor criar um cursor e dentro de cada linha do cursor eu executo a instrução SQL mas passando uma condição bem restritiva no WHERE baseado no valor do cursor fazendo o SQL Server gerar um plano de execução com vários Seeks o que foi bem melhor que apenas um Index Scan.
Fiz mais outras alterações como trocar os DataTypes das tabelas temporárias usadas na procedure DateTime para SmallDateTime, Int para SmallInt ou TyniInt, SET NOCOUNT ON dentre outros, mas o ideal é sempre melhorar o gargalo.
A melhora foi muito grande, vamos analisar os resultados no profiler.
Conclusão de 2:20 minutos para 0. J
Verificamos então como identificar o problema, fica a dica sobre Statistics e a analise dos planos de execução utilizando o CTRL-M, é claro que isso não funciona sempre mas ajuda.
__________________________________________________________________
Fabiano Neves Amorim (MCP – MCTS – SQL Server)
Análise – NewCon Enterprise
* fabiano@cnpm.com.br – http://fabianosqlserver.spaces.live.com/
( (55 – 14) 3404-3700