Introdução
As estatísticas são fundamentais em qualquer banco de dados. São com elas que se torna possível criar o melhor plano de execução possível para as consultas, para que isso aconteça, é necessário que as mesmas estejam sempre atualizadas.
Atualização de Estatísticas no SQL Server
As atualizações de estatísticas no SQL Server são realizadas de forma automática. o AUTO-CREATE e o AUTO-UPDATE STATISTICS estão habilitados por padrão no SQL Server e quando é necessário, as mesmas são atualizadas. Existe muito mais conteúdo sobre estatísticas – https://www.simple-talk.com/sql/learn-sql-server/statistics-in-sql-server/, http://blogfabiano.com/?s=estatisticas, mas nesse post iremos falar sobre as estatísticas vinculadas ao recurso In-Memory OLTP do SQL Server 2014 chamado Hekaton.
Atualização de Estatísticas no Hekaton
A criação de tabelas In-Memory é diferente da criação das tabelas On-Disk. A criação da tabela In-Memory é compilada e colocada direto em .DLL assim temos uma velocidade de execução de 2x a 3x, isso porque todos os bancos de dados possuem do que chamamos de INTEPRETER. O interpretador em um SGDB é responsável por traduzir o código que foi escrito e passar para linguagem de máquina. Quando criamos as tabelas In-Memory não precisamos do interpretador porque a mesma é persistida direto em código de máquina. Ganhamos em velocidade de execução que é o principal motivo de ter as tabelas em memória, porém temos que nos alertar em diversos detalhes no qual hã grande impacto caso não tratemos um a um, as estatísticas é uma delas.
Alguns pontos importantes sobre estatísticas In-Memory Tables
Estatísticas não são geradas em tabelas em memória
Não são auto geradas
Não são auto atualizáveis
Realizando a consulta abaixo iremos reparar que o Plano de Execução nos alerta alguns detalhes durante sua execução.
SELECT inmem_DAC.NumeroAtendimento,
inmem_DAC.StatusAtendimento,
inmem_VP.Name,
SUM(LineTotal – UnitPrice) AS ProductValue,
SUM(inmem_VP.LineTotal) AS LineTotal,
SUM(inmem_VP.UnitPrice) AS UnitPrice,
COUNT(*) AS Quantidade
FROM inmem_DadosAtendimentoClientes AS inmem_DAC
LEFT OUTER JOIN inmem_VendasProdutos AS inmem_VP
ON inmem_DAC.ID = inmem_VP.SalesOrderID
WHERE StatusAtendimento = ‘Baixado’
GROUP BY inmem_DAC.NumeroAtendimento, inmem_DAC.StatusAtendimento, inmem_VP.Name
HAVING SUM(inmem_VP.UnitPrice) > 2049.0982
ORDER BY inmem_DAC.NumeroAtendimento DESC
(Figura 1 – Plano de Execução da Consulta alertando um problema no Hash Match, 25%.)
Reparem que existe um alerta no operador Hash Match do plano de execução, indo mais fundo no problema vemos que…
(Figura 2 – Detalhes do plano de execução do operador Hash Match.)
Notem que o ACTUAL NUMBER OF ROWS = 1.397.041 e o ESTIMATED NUMBER OF ROWS = 261.100 estão completamente errados, isso acontece porque como dito anteriormente as tabelas In-Memory não realizam a atualização de estatísticas. Além de ter estimado o valor errado o que faz com que o plano de execução não seja o mais eficiente o mesmo mostra um WARNING muito custoso. O WARNING mostra “Operator used tempdb to spill data during execution with spill level 1.” Isso quer dizer que a tabela In-Memory está jogando o JOIN no tempDB para realizar a comparação entre as tabelas, assim o custo total da consulta fica extremamente custosa.
Para que não haja esse problema novamente, sempre que for realizado grandes inserções em tabelas In-Memory é necessário que a mesma seja atualizada, com isso se realizarmos a atualização de estatísticas nesse caso iremos reparar que…
Atualizando as Estatísticas…
UPDATE STATISTICS inmem_DadosAtendimentoClientes WITH FULLSCAN, NORECOMPUTE
UPDATE STATISTICS inmem_VendasProdutos WITH FULLSCAN, NORECOMPUTE
Realizando o SELECT novamente…
SELECT inmem_DAC.NumeroAtendimento,
inmem_DAC.StatusAtendimento,
inmem_VP.Name,
SUM(LineTotal – UnitPrice) AS ProductValue,
SUM(inmem_VP.LineTotal) AS LineTotal,
SUM(inmem_VP.UnitPrice) AS UnitPrice,
COUNT(*) AS Quantidade
FROM inmem_DadosAtendimentoClientes AS inmem_DAC
LEFT OUTER JOIN inmem_VendasProdutos AS inmem_VP
ON inmem_DAC.ID = inmem_VP.SalesOrderID
WHERE StatusAtendimento = ‘Baixado’
GROUP BY inmem_DAC.NumeroAtendimento, inmem_DAC.StatusAtendimento, inmem_VP.Name
HAVING SUM(inmem_VP.UnitPrice) > 2049.0982
ORDER BY inmem_DAC.NumeroAtendimento DESC
(Figura 3 – Plano de Execução da Consulta com Hash Match, agora com 3%.)
(Figura 4 – Detalhes do plano de execução do operador Hash Match após atualizado.)
OBS – O Operador Hash Match custava 25% e agora está custando somente 3%
Agora o ACTUAL NUMBER OF ROWS e o ESTIMATED NUMBER OF ROWS estão equivalente após a atualização de estatísticas até porque o HISTOGRAMA foi atualizado, além disso não foi gerado o WARNING anterior da consulta e o custo do Hash Match agora é de 3%.
Com isso, sempre que a tabelas em memória (In-Memory Tables) sofrerem atualizações, realize a atualização de estatísticas, se isso for esquecido as consultas em tabelas On-Disk estaram provavelmente mais rápidas que as consultas em tabelas In-Memory.
Igor Antonio
junho 10, 2014
Um tópico bem interessante, ainda não tinha parado para pensar em como o MSSQL gerenciava estatísticas do In-Memory. Sempre se pensa em “o padrão” em primeiro lugar, mas como pode-se ver nem sempre é assim… Belo artigo, adicionado aos favoritos, muito obrigado.
Luan.Moreno [SQL Soul]
junho 10, 2014
Igor, boa tarde!
Verdade realmente por padrão pensamos assim mas com In-Memory temos que tomar cuidado, imagine um SPILL na tempDB em uma tabela de 3 MI de registros. Realmente muito custoso, ainda vem mais coisa pela frente, que bom que gostou obrigado pelo feedback ! 🙂
Lucas Souza
junho 11, 2014
Show Luan ! Post bem objetivo.