Database
 sql >> Base de Dados >  >> RDS >> Database

Entendendo o que sp_updatestats realmente atualiza


Quando estive em Chicago há algumas semanas para um de nossos eventos de imersão, um participante fez uma pergunta sobre estatísticas. Não entrarei em todos os detalhes sobre o problema, mas o participante mencionou que as estatísticas foram atualizadas usando sp_updatestats . Este é um método para atualizar estatísticas que nunca recomendei; Sempre recomendei uma combinação de recompilações de índice e UPDATE STATISTICS para manter as estatísticas atualizadas. Se você não estiver familiarizado com sp_updatestats , é um comando que é executado para todo o banco de dados para atualizar as estatísticas. Mas, como Kimberly apontou para o participante, sp_updatestats atualizará uma estatística desde que tenha uma linha modificada. Uau. Imediatamente abri o Books Online e para sp_updatestats você verá isso:
sp_updatestats atualiza apenas as estatísticas que exigem atualização com base nas informações de rowmodctr na exibição de catálogo sys.sysindexes, evitando atualizações desnecessárias de estatísticas em linhas inalteradas
Agora, admito, fiz uma suposição sobre o que significava "...exigir atualização com base nas informações rowmodctr na exibição de catálogo sys.sysindexes...". Eu assumi que a decisão de atualização seguiria a mesma lógica que a opção Auto Update Statistics segue, que é:
  • O tamanho da tabela passou de 0 para>0 linhas (teste 1).
  • O número de linhas na tabela quando as estatísticas foram coletadas era 500 ou menos, e o colmodctr da coluna principal do objeto de estatísticas mudou em mais de 500 desde então (teste 2).
  • A tabela tinha mais de 500 linhas quando as estatísticas foram coletadas e o colmodctr da coluna principal do objeto de estatísticas mudou em mais de 500 + 20% do número de linhas na tabela quando as estatísticas foram coletadas ( teste 3).

Esta lógica não é seguida para sp_updatestats . Na verdade, a lógica é tão incrivelmente simples que é assustadora:se uma linha for modificada, a estatística será atualizada. Uma linha. UMA LINHA. Qual é a minha preocupação? Estou preocupado com a sobrecarga de atualizar estatísticas para um monte de estatísticas que realmente não precisam ser atualizadas. Vamos dar uma olhada em sp_updatestats .

Começaremos com uma nova cópia do banco de dados AdventureWorks2012 que você pode baixar do Codeplex. Vou primeiro atualizar linhas em três tabelas diferentes:
USE [AdventureWorks2012];
GO
SET NOCOUNT ON;
GO
 
UPDATE [Production].[Product]
SET [Name] = 'Bike Chain'
WHERE [ProductID] = 952;
 
UPDATE [Person].[Person]
SET [LastName] = 'Cameron'
WHERE [LastName] = 'Diaz';
GO
 
INSERT INTO Sales.SalesReason
(Name, ReasonType, ModifiedDate)
VALUES('Stats', 'Test', GETDATE());
GO 10000

Modificamos uma linha em Production.Product , 211 linhas em Person.Person , e adicionamos 10.000 linhas a Sales.SalesReason . Se o sp_updatestats procedimento seguiu a mesma lógica para atualizações que a opção Estatísticas de atualização automática, então somente Sales.SalesReason atualizaria porque tinha 10 linhas para iniciar (enquanto as 211 linhas atualizadas em Person.Person representam cerca de um por cento da tabela). No entanto, se investigarmos sp_updatestats , podemos ver que a lógica utilizada é diferente. Observe que estou apenas extraindo as instruções de dentro de sp_updatestats que são usados ​​para determinar quais estatísticas são atualizadas.

Um cursor percorre todas as tabelas definidas pelo usuário e tabelas internas no banco de dados:
declare ms_crs_tnames cursor local fast_forward read_only for
select name, object_id, schema_id, type from sys.objects o
where o.type = 'U' or o.type = 'IT'
open ms_crs_tnames
fetch next from ms_crs_tnames into @table_name, @table_id, @sch_id, @table_type

Outro cursor percorre as estatísticas de cada tabela e exclui heaps e índices e estatísticas hipotéticos. Observe que sys.sysindexes é usado em sp_helpstats . Sysindexes é uma tabela de sistema do SQL Server 2000 e está agendada para ser removida em uma versão futura do SQL Server. Isso é interessante, pois o outro método para determinar as linhas atualizadas é o sys.dm_db_stats_properties DMF, que só está disponível no SQL 2008 R2 SP2 e SQL 2012 SP1.
set @index_names = cursor local fast_forward read_only for
select name, indid, rowmodctr
from sys.sysindexes
where id = @table_id
and indid > 0
and indexproperty(id, name, 'ishypothetical') = 0
order by indid

Após um pouco de preparação e lógica adicional, chegamos a um IF declaração que revela que sp_updatestats filtra as estatísticas que não tiveram nenhuma linha atualizada... confirmando que mesmo que apenas uma linha tenha sido modificada, a estatística será atualizada. Há também uma verificação para @is_ver_current , que é determinado por uma função interna interna.
if ((@ind_rowmodctr <> 0) or ((@is_ver_current is not null) and (@is_ver_current = 0)))

Mais algumas verificações relacionadas ao nível de amostragem e compatibilidade e, em seguida, o UPDATE instrução é executada para a estatística. Antes de realmente executar sp_updatestats, podemos consultar sys.sysindexes para ver quais estatísticas serão atualizadas:
SELECT [o].[name], [si].[indid], [si].[name], [si].[rowmodctr], [si].[rowcnt], [o].[type]
FROM [sys].[objects] [o]
JOIN [sys].[sysindexes] [si] ON [o].[object_id] = [si].[id]
WHERE ([o].[type] = 'U' OR [o].[type] = 'IT')
AND [si].[indid] > 0
AND [si].[rowmodctr] <> 0
ORDER BY [o].[type] DESC, [o].[name];

Além das três tabelas que modificamos, há outra estatística para uma tabela de usuário (dbo.DatabaseLog ) e três estatísticas internas que serão atualizadas:


Estatísticas que serão atualizadas

Se executarmos sp_updatestats para o banco de dados AdventureWorks, a saída lista todas as tabelas e as estatísticas atualizadas. A saída abaixo foi modificada para mostrar apenas estatísticas atualizadas:


Atualizando [sys].[fulltext_avdl_1589580701]
[clust] foi atualizado…
1 índice(s)/estatística(s) foi(m) atualizado(s), 0 não exigiu atualização.



Atualizando [dbo].[DatabaseLog]
[PK_DatabaseLog_DatabaseLogID] foi atualizado…
1 índice(s)/estatística(s) foi(m) atualizado(s), 0 não exigiu atualização.



Atualizando [sys].[fulltext_avdl_1077578877]
[clust] foi atualizado…
1 índice(s)/estatística(s) foi(m) atualizado(s), 0 não exigiu atualização.



Atualizando [Person].[Person]
[PK_Person_BusinessEntityID], atualização não é necessária…
[IX_Person_LastName_FirstName_MiddleName] foi atualizado…
[AK_Person_rowguid], atualização não é necessária…
1 índice(es)/estatística(s) foram atualizados, 2 não necessitaram de atualização.



Atualizando [Sales].[SalesReason]
[PK_SalesReason_SalesReasonID] foi atualizado…
1 índice(s)/estatística(s) foi(m) atualizado(s), 0 não exigiu atualização.



Atualizando [Production].[Product]
[PK_Product_ProductID], atualização não é necessária…
[AK_Product_ProductNumber], atualização não é necessária…
[AK_Product_Name] foi atualizado…
[ AK_Product_rowguid], atualização não é necessária…
[_WA_Sys_00000013_75A278F5], atualização não é necessária…
[_WA_Sys_00000014_75A278F5], atualização não é necessária…
[_WA_Sys_0000000D_75A278F5], atualização não é necessária…
[_WA_Sys_0000000C_75A278F5], a atualização não é necessária…
1 índice(s)/estatística(s) foi(m) atualizado(s), 7 não necessitaram de atualização.



As estatísticas de todas as tabelas foram atualizadas.

A última linha da saída é um pouco enganosa – as estatísticas de todas as tabelas não foram atualizadas, apenas as estatísticas que tiveram uma linha ou mais modificadas foram atualizadas. E, novamente, a desvantagem disso é que talvez tenham sido usados ​​recursos que não precisavam ser usados. Se uma estatística tiver apenas uma linha modificada, ela deve ser atualizada? Não. Se tiver 10.000 linhas atualizadas, deve ser atualizado? Bem, isso depende. Se a tabela tiver apenas 5.000 linhas, absolutamente; se a tabela tiver 1 milhão de linhas, não, pois apenas um por cento da tabela foi modificado.

A conclusão aqui é que, se você estiver usando sp_updatestats para atualizar suas estatísticas, você provavelmente está desperdiçando recursos, incluindo CPU, E/S e tempdb. Além disso, leva tempo para atualizar cada estatística e, se você tiver uma janela de manutenção apertada, provavelmente terá outras tarefas de manutenção que podem ser executadas nesse período, em vez de atualizações desnecessárias. Por fim, você provavelmente não está fornecendo nenhum benefício de desempenho atualizando as estatísticas quando tão poucas linhas foram alteradas. A mudança de distribuição é provavelmente insignificante se apenas uma pequena porcentagem de linhas tiver sido modificada, de modo que os valores de histograma e densidade não acabem mudando muito. Além disso, lembre-se de que a atualização das estatísticas invalida os planos de consulta que usam essas estatísticas. Quando essas consultas são executadas, os planos são gerados novamente e provavelmente o plano será exatamente o mesmo de antes, pois não houve alteração significativa no histograma. Há um custo para recompilar planos de consulta – nem sempre é fácil de medir, mas não deve ser ignorado.

Um método melhor para gerenciar estatísticas – porque você precisa gerenciar estatísticas – é implementar um trabalho agendado que é atualizado com base nas porcentagens de linhas que foram modificadas. Você pode usar a consulta acima mencionada que interroga sys.sysindexes , ou você pode usar a consulta abaixo que aproveita o novo DMF adicionado no SQL Server 2008 R2 SP2 e SQL Server 2012 SP1:
SELECT [sch].[name] + '.' + [so].[name] AS [TableName] ,
[ss].[name] AS [Statistic],
[sp].[last_updated] AS [StatsLastUpdated] ,
[sp].[rows] AS [RowsInTable] ,
[sp].[rows_sampled] AS [RowsSampled] ,
[sp].[modification_counter] AS [RowModifications]
FROM [sys].[stats] [ss]
JOIN [sys].[objects] [so] ON [ss].[object_id] = [so].[object_id]
JOIN [sys].[schemas] [sch] ON [so].[schema_id] = [sch].[schema_id]
OUTER APPLY [sys].[dm_db_stats_properties]([so].[object_id],
[ss].[stats_id]) sp
WHERE [so].[type] = 'U'
AND [sp].[modification_counter] > 0
ORDER BY [sp].[last_updated] DESC;

Perceba que tabelas diferentes podem ter limites diferentes e você precisará ajustar a consulta acima para seus bancos de dados. Para algumas tabelas, esperar até que 15% ou 20% das linhas sejam modificadas pode ser aceitável. Mas para outros, pode ser necessário atualizar em 10% ou até 5%, dependendo dos valores reais e de sua inclinação. Não há bala de prata. Por mais que gostemos de absolutos, eles raramente existem no SQL Server e as estatísticas não são exceção. Você ainda deseja deixar as estatísticas de atualização automática ativadas - é uma segurança que será ativada se você perder alguma coisa, assim como o crescimento automático para seus arquivos de banco de dados. Mas sua melhor aposta é conhecer seus dados e implementar uma metodologia que permita atualizar estatísticas com base na porcentagem de linhas alteradas.