Temos vários bancos de dados nos quais um grande número de tabelas é criado e descartado. Pelo que sabemos, o SQL Server não realiza nenhuma manutenção interna nas tabelas base do sistema , o que significa que elas podem se tornar muito fragmentadas ao longo do tempo e inchadas em tamanho. Isso coloca pressão desnecessária no buffer pool e também afeta negativamente o desempenho das operações, como calcular o tamanho de todas as tabelas em um banco de dados.
Alguém tem sugestões para minimizar a fragmentação nessas tabelas internas principais? Uma solução óbvia poderia evitar criar tantas tabelas (ou criar todas as tabelas transitórias no tempdb), mas, para o propósito desta pergunta, digamos que o aplicativo não tenha essa flexibilidade.
Edit: Mais pesquisas mostram essa pergunta sem resposta , que parece intimamente relacionada e indica que alguma forma de manutenção manual via ALTER INDEX...REORGANIZE
pode ser uma opção.
Pesquisa inicial
Os metadados sobre essas tabelas podem ser visualizados em sys.dm_db_partition_stats
:
-- The system base table that contains one row for every column in the system
SELECT row_count,
(reserved_page_count * 8 * 1024.0) / row_count AS bytes_per_row,
reserved_page_count/128. AS space_mb
FROM sys.dm_db_partition_stats
WHERE object_id = OBJECT_ID('sys.syscolpars')
AND index_id = 1
-- row_count: 15,600,859
-- bytes_per_row: 278.08
-- space_mb: 4,136
No entanto, sys.dm_db_index_physical_stats
não parece suportar a exibição da fragmentação dessas tabelas:
-- No fragmentation data is returned by sys.dm_db_index_physical_stats
SELECT *
FROM sys.dm_db_index_physical_stats(
DB_ID(),
OBJECT_ID('sys.syscolpars'),
NULL,
NULL,
'DETAILED'
)
Os scripts de Ola Hallengren também contêm um parâmetro para considerar a desfragmentação de is_ms_shipped = 1
objetos, mas o procedimento ignora silenciosamente as tabelas base do sistema, mesmo com esse parâmetro ativado. Ola esclareceu que esse é o comportamento esperado; somente tabelas de usuário (não tabelas de sistema) que são ms_shipped (por exemplo msdb.dbo.backupset
) são consideradas.
-- Returns code 0 (successful), but does not do any work for system base tables.
-- Instead of the expected commands to update statistics and reorganize indexes,
-- no commands are generated. The script seems to assume the target tables will
-- appear in sys.tables, but this does not appear to be a valid assumption for
-- system tables like sys.sysrowsets or sys.syscolpars.
DECLARE @result int;
EXEC @result = IndexOptimize @Databases = 'Test',
@FragmentationLow = 'INDEX_REORGANIZE',
@FragmentationMedium = 'INDEX_REORGANIZE',
@FragmentationHigh = 'INDEX_REORGANIZE',
@PageCountLevel = 0,
@UpdateStatistics = 'ALL',
@Indexes = '%Test.sys.sysrowsets.%',
-- Proc works properly if targeting a non-system table instead
--@Indexes = '%Test.dbo.Numbers.%',
@MSShippedObjects = 'Y',
@Execute = 'N';
PRINT(@result);
Informações adicionais solicitadas
Usei uma adaptação da consulta de Aaron abaixo do uso do pool de buffers da tabela do sistema, e isso constatou que existem dezenas de GB de tabelas do sistema no buffer pool para apenas um banco de dados, com ~ 80% desse espaço sendo espaço livre em alguns casos .
-- Compute buffer pool usage by system table
SELECT OBJECT_NAME(p.object_id),
COUNT(b.page_id) pages,
SUM(b.free_space_in_bytes/8192.0) free_pages
FROM sys.dm_os_buffer_descriptors b
JOIN sys.allocation_units a
ON a.allocation_unit_id = b.allocation_unit_id
JOIN sys.partitions p
ON p.partition_id = a.container_id
AND p.object_id < 1000 -- A loose proxy for system tables
WHERE b.database_id = DB_ID()
GROUP BY p.object_id
ORDER BY pages DESC
fonte
Com base nas orientações da resposta de Aaron, bem como em pesquisas adicionais, aqui está um rápido resumo da abordagem adotada.
Pelo que sei, as opções para inspecionar a fragmentação das tabelas base do sistema são limitadas. Fui adiante e registrei um problema do Connect para fornecer melhor visibilidade, mas, enquanto isso, parece que as opções incluem coisas como examinar o buffer pool ou verificar o número médio de bytes por linha.
Em seguida, criei um procedimento para executar `ALTER INDEX ... REORGANIZE em todas as tabelas base do sistema . A execução deste procedimento em alguns dos nossos servidores de desenvolvimento mais (ab) usados mostrou que o tamanho cumulativo das tabelas base do sistema foi cortado em até 50 GB (com ~ 5MM tabelas de usuário no sistema, portanto, claramente um caso extremo).
Uma de nossas tarefas de manutenção noturna, que ajuda a limpar muitas das tabelas de usuários criadas por vários testes e desenvolvimento de unidades, estava demorando aproximadamente 50 minutos para ser concluída. Uma combinação de
sp_whoisactive
,sys.dm_os_waiting_tasks
eDBCC PAGE
mostrou que as esperas eram dominadas por E / S nas tabelas base do sistema.Após a reorganização de todas as tabelas base do sistema, a tarefa de manutenção caiu para ~ 15 minutos. Ainda havia algumas esperas de E / S, mas elas foram significativamente diminuídas, talvez devido a uma quantidade maior de dados restantes no cache e / ou mais readaheads devido à menor fragmentação.
Portanto, minha conclusão é que adicionar
ALTER INDEX...REORGANIZE
tabelas de base do sistema a um plano de manutenção pode ser uma coisa útil a considerar, mas provavelmente apenas se você tiver um cenário em que um número incomum de objetos esteja sendo criado em um banco de dados.fonte