Create Index não é Tuning!
Boa noite, galera, tudo bem?
Muita saudades de postar, mas, a correria esta demais. Prometo que vai ser mais frequente daqui pra frente. Gostaria de dedicar esse post para a equipe do Oracle (Carlos Carvalho, Anderson Ferreira, Alessandro Andrieta e Ricardo Argona) que trabalha comigo, estou aprendendo muito com eles, por exemplo, que produto Não utilizar…rs, brincadeira.
Hoje gostaria de falar sobre um assunto que é muito comum no mercado de trabalho. O DBA cria um índice elimina um Key Lookup e se diz “especialista” em performance. Não quero julgar ninguém, mas, performance e tuning vai muito mais além do que isso, e eu gostaria de demonstrar isso nesse post.
Tenho uma procedure que tem uma instrução UPDATE que faz parte de 56,7% de custo do total da execução da procedure:
update A
SET A.SPRD_LIQ_ACUM_M_ATU = B.RES_SPREAD_LIQUIDACAO,
A.RES_REF_LIQ_ACUM_M_ATU = B.RES_GAP_LIQUIDACAO
FROM #TB_DATA_BASE A
INNER JOIN
( SELECT ESTOQUE.ID_OPERACAO_DRCG, ESTOQUE.NO_PARCELA, ESTOQUE.IC_PAG_REC,
RES_SPREAD_LIQUIDACAO = SUM(ESTOQUE.VL_RES_SPREAD_LIQUIDACAO),
RES_GAP_LIQUIDACAO = SUM(ESTOQUE.VL_RES_GAP_LIQUIDACAO)
FROM ESTOQUE_RES_RF ESTOQUE WITH(NOLOCK)
INNER JOIN #TB_DATA_BASE A ON ESTOQUE.ID_OPERACAO_DRCG = A.ID_OPERACAO_DRCG
WHERE ESTOQUE.DT_ESTOQUE <= @PDT_BASE AND A.NO_PARCELA = ESTOQUE.NO_PARCELA
AND A.IC_PAG_REC = ESTOQUE.IC_PAG_REC
GROUP BY ESTOQUE.ID_OPERACAO_DRCG, ESTOQUE.NO_PARCELA, ESTOQUE.IC_PAG_REC) AS B
ON A.ID_OPERACAO_DRCG = B.ID_OPERACAO_DRCG
AND A.NO_PARCELA = B.NO_PARCELA
AND A.IC_PAG_REC = B.IC_PAG_REC
Analisando o plano de execução , podemos notar que a query que mais consomem mais recurso do plano. Vamos primeiro avaliar o maior vilão que no case é o update demonstrado anteriormente. Como pode ser visto no print, o SQL Server está realizando um Clustered Index Seek na tabela estoque_res_rf e essa operação está fazendo uma leitura de 711.152.000 registros. Porém, no final do processo, podemos perceber que o SQL Server apenas precisa de 48.558 registros. Esses registros são frutos do Join com a tabela temporária #TB_DATA_BASE. Thiago, que dizer que estou lendo mais registros do que preciso? A resposta é: SIM.
Analisando o histograma de estatística do SQL Server, podemos ver que a estatística está Atualizada, mesmo que não seja FULLSCAN. Então provavelmente o problema não foi causado por estatísticas desatualizadas. O SQL Server está fazendo exatamente o que deveria ser feito. Vamos ver? Executo o comando que mostra as estatísticas do Índice pk_estoque_res_rf da Tabela ESTOQUE_RES_RF, que foi requisitada no print anterior.
DBCC SHOW_STATISTICS (ESTOQUE_RES_RF,pk_estoque_res_rf)
O histograma abaixo, mostra a atualização das estatísticas e qual o seu último step no histograma do SQL Server que tem o RANGE_HI_KEY 2013-05-13 00:00:00.000, que é exatamente a Data Base utilizada como filtro para os comandos de UPDATE (descrito no inicio deste documento).
O filtro da query está utilizando um operador de “<= @dataBase”, e O SQL Serve tem que estimar TUDO menor que isso,nessa ocasião ele realiza a soma das colunas RANGE_ROWS e EQ_ROWS para chegar nesse valor. Por exemplo: 1403920 + 750,6796 + 1413610 até o primeiro step do histograma, assim , podendo abranger todos os valores.
Realizando o cálculo no excel a soma foi algo em torno de 712.633.500 registros (O Valor não é o mesmo que o do plano acima, pois, ele foi estimado ontem e os dados já não são mais os mesmos do momento do plano). Nesse caso, apenas criar um indice não resolveria, pois, o SQL Server continuaria lendo todas as informações menor que a data especificada no parametro e, provavelmente o SLQ Sever optaria para ler o indice que contem mais páginas de dados, no caso o Clustered.
Nesse caso a sugestão seria Avaliar a alteração da query para adicionar filtros mais seletivos, evitando assim, a leitura total do Indice. O que poderia ser feito é adicionar um filtro para trazer valor “>= “ a partir de uma data “X”. Realizando um teste para a data ‘2013-04-01 00:00.00.000’ e tive o seguinte resultado:
A pergunta é: no primeiro print o SQL Server fez um Clustered Index Seek para ler toda as informações da tabela, não seria mais fácil apenas realizar um Clustered Index Scan? Ess será assunto para o próximo post. Eu irei mostrar porque o SQL Server escolheu o Seek ao invés do Scan, uma coisa é fato: Para esse caso o Seek é mais eficiente, a pergunta é porque?
7 comentários
Tu é muito fera meu amigo!
Valeu Thi por ter lembrado da melhor equipe de Oracle do Itau!
Ah cara parabens belo post e vc eh um pita profissional qualificado! E sabe o que faz!
[…] Create Index não é Tuning! […]
Tiago,
seria legal você postar as duas querys para que todos pudessem ver como ficou. Eu entendi perfeitamente o que você quis dizer e realmente é isso mesmo. Só criar o índice, digitar sp_recompile e eliminar o lookup já ajuda, mas não é tudo . rs
Eliminar funções sobre colunas no where e modelar a tabela correta são exemplos de técnicas de tuning que podem ser utilizadas.
No mais, parabéns pelo artigo. Muito legal !
Valeu brizola. Vou tentar fazer isso nos próximos exemplos. Vou dar uma consultada nos meus arquivos, pra ver se encontro as queries.
Valeu… Qualquer dúvida manda ai…
Bom dia Thiago.
gostei muito do blog, em especifico a parte de Tuning. Estou fazendo minha monografia e gostaria muito de uma ajuda, gostaria de um contato seu para pedir uma ajuda, caso você possa dar. pode ser email, skype ou qualquer outra ferramenta de contato.
muito grato e aguardo retorno. a proposito adicionei no linkedin.
Tiago Gomes.
Fala Tiago Gomes. Blz? Cara se eu ainda puder te ajudar… segue meus contatos. [email protected]. Desculpa a demora pra responder.