Reorganizar o índice vs Reconstruir o Índice no Sql Server plano de Manutenção
-
08-06-2019 - |
Pergunta
No SSW regras para melhor Banco de dados SQL Server, há um exemplo de um banco de dados completo plano de manutenção: SSW.No exemplo que executar um Reorganizar Índice e, em seguida, uma Reconstrução de Índice e, em seguida, Atualização de Estatísticas.Há qualquer razão para isso?Eu pensei que Reorganizar o Índice foi rápido, mas menos eficaz versão de Reconstruir o Índice?e que uma reconstrução de índice gostaria também de atualizar as estatísticas automaticamente (no índice agrupado pelo menos).
Solução
Fazendo um REORGANIZE
e, em seguida, um REBUILD
no mesmo índices é inútil, pois qualquer alteração pelo REORGANIZE
seria perdido, fazendo o REBUILD
.
Pior que isso é que, no plano de manutenção diagrama de SSW, ele executa um SHRINK
primeiro, que os fragmentos de índices como um efeito colateral da maneira de liberar espaço.Em seguida, o REBUILD
aloca mais espaço para os arquivos de banco de dados novamente como espaço de trabalho durante a REBUILD
operação.
REORGANIZE
é uma operação online que desfragmenta páginas de folha em um cluster ou índice não-agrupado por página usando pouco mais de espaço de trabalho.REBUILD
é uma operação online nas edições Enterprise, offline, em outras edições, e usa como extra de espaço de trabalho, como o tamanho do índice.Ele cria uma nova cópia do índice e, em seguida, descarta o antigo, assim livrar-se da fragmentação.As estatísticas são recalculados por padrão como parte desta operação, mas que pode ser desativado.
Ver A reorganização e a Reconstrução de Índices para obter mais informações.
Não use SHRINK
exceto com o TRUNCATEONLY
opção e, mesmo assim, se o arquivo voltará a crescer em seguida, você deve pensar muito para saber se é necessário:
Outras dicas
A reorganizar e recriar são coisas diferentes.
Reorganizar:é uma desfragmentação para índices.Leva o índice já existente(es) e desfragmenta as páginas existentes.No entanto, se as páginas não são de maneira contígua, que permanece como antes.Apenas o conteúdo das páginas estão mudando.
Reconstruir:na verdade, ele ignora o índice e recria-lo a partir do zero.Isso significa que você vai ter um completamente novo índice, com desfragmentado e páginas contíguas.
Além disso, com reconstrução pode alterar o particionamento ou grupos de arquivos, mas com reorganizar você pode desfragmentar não só o índice inteiro, mas também apenas uma partição de índice.
O update statistics é automático índices em cluster, mas não no não-agrupado queridos.
Antes de considerar a manutenção de índices, é importante responder a duas perguntas principais:
- Qual é o grau de fragmentação?
- O que é a ação apropriada?Reorganizar ou reconstruir?
Como descrito neste artigo http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/, e para ajudar a determinar se você deve executar a reconstrução de índice ou de índice de reorganização, por favor, entenda o seguinte:
Índice de reorganização é um processo onde o SQL Server passa por índice existente, e limpa-lo.Reconstrução de índice é um serviço pesado processo onde o índice é excluído e, em seguida, recriado a partir do zero com uma nova estrutura, livre de todos amontoados fragmentos e vazio-espaço páginas.
Enquanto o índice de reorganização é uma pura operação de limpeza, que deixa o estado do sistema como ele é, sem fecho-as tabelas afetadas e pontos de vista, o processo de reconstrução bloqueios de tabela afetado para todo o período de reconstrução, o que pode resultar em muito tempo tempo que não pode ser aceitável em alguns ambientes.Com isso em mente, é claro que a reconstrução de índice é um processo com 'mais forte' solução, mas ele vem com um preço – possível longas madeixas em afetado tabelas indexadas.
Por outro lado, a reorganização de índice é um 'leve' processo que vai resolver a fragmentação de uma maneira menos eficaz – desde limpo índice será sempre a segunda para o novo totalmente feito do zero.Mas a reorganização do índice é muito melhor do ponto de vista de eficiência, uma vez que não bloqueio afetados tabela indexada durante o curso da operação.
O artigo acima mencionado, que também explica como reorganizar e reconstruir índices usando o SSMS, T-SQL (para reorganizar/recriação de índices em uma tabela) e um 3rd party ferramenta chamada ApexSQL de Cópia de segurança.
Ao fazer uma reorganização de um índice, se o índice é transmitida através de dois ou mais arquivos físicos os dados só serão defragged dentro do arquivo de dados.As páginas não são movidas a partir de um arquivo de dados para outro.
Quando o índice é, em um único arquivo a reorganização e reindexar vai ter o mesmo resultado final.
Algumas vezes a reorganização será mais rápido, e algumas vezes o reindex vai ser mais rápido, dependendo do grau de fragmentação de índice.A menos de fragmentação de índice, em seguida, uma reorganização será mais rápido, mais fragmentada, mais lenta será a reorganização será, mas o mais rapidamente um reindex vai ser.
Exatamente o que Biri disse.Aqui está como eu gostaria de reindexar um banco de dados inteiro:
EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')"
Eu Uso este SP
CREATE PROCEDURE dbo.[IndexRebuild]
AS
DECLARE @TableName NVARCHAR(500);
DECLARE @SQLIndex NVARCHAR(MAX);
DECLARE @RowCount INT;
DECLARE @Counter INT;
DECLARE @IndexAnalysis TABLE
(
AnalysisID INT IDENTITY(1, 1)
NOT NULL
PRIMARY KEY ,
TableName NVARCHAR(500) ,
SQLText NVARCHAR(MAX) ,
IndexDepth INT ,
AvgFragmentationInPercent FLOAT ,
FragmentCount BIGINT ,
AvgFragmentSizeInPages FLOAT ,
PageCount BIGINT
)
BEGIN
INSERT INTO @IndexAnalysis
SELECT [objects].name ,
'ALTER INDEX [' + [indexes].name + '] ON ['
+ [schemas].name + '].[' + [objects].name + '] '
+ ( CASE WHEN ( [dm_db_index_physical_stats].avg_fragmentation_in_percent >= 20
AND [dm_db_index_physical_stats].avg_fragmentation_in_percent < 40
) THEN 'REORGANIZE'
WHEN [dm_db_index_physical_stats].avg_fragmentation_in_percent > = 40
THEN 'REBUILD'
END ) AS zSQL ,
[dm_db_index_physical_stats].index_depth ,
[dm_db_index_physical_stats].avg_fragmentation_in_percent ,
[dm_db_index_physical_stats].fragment_count ,
[dm_db_index_physical_stats].avg_fragment_size_in_pages ,
[dm_db_index_physical_stats].page_count
FROM [sys].[dm_db_index_physical_stats](DB_ID(), NULL, NULL,
NULL, 'LIMITED') AS [dm_db_index_physical_stats]
INNER JOIN [sys].[objects] AS [objects] ON ( [dm_db_index_physical_stats].[object_id] = [objects].[object_id] )
INNER JOIN [sys].[schemas] AS [schemas] ON ( [objects].[schema_id] = [schemas].[schema_id] )
INNER JOIN [sys].[indexes] AS [indexes] ON ( [dm_db_index_physical_stats].[object_id] = [indexes].[object_id]
AND [dm_db_index_physical_stats].index_id = [indexes].index_id
)
WHERE index_type_desc <> 'HEAP'
AND [dm_db_index_physical_stats].avg_fragmentation_in_percent > 20
END
SELECT @RowCount = COUNT(AnalysisID)
FROM @IndexAnalysis
SET @Counter = 1
WHILE @Counter <= @RowCount
BEGIN
SELECT @SQLIndex = SQLText
FROM @IndexAnalysis
WHERE AnalysisID = @Counter
EXECUTE sp_executesql @SQLIndex
SET @Counter = @Counter + 1
END
GO
e criar Um Trabalho que executar esta SP a cada semana.
O melhor mesmo é:
EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX'
ou
EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE'
Meus dois centavos...Este método segue as especificações descritas no tech net: http://technet.microsoft.com/en-us/library/ms189858(v=sql.105).aspx
USE [MyDbName]
GO
SET ANSI_NULLS OFF
GO
SET QUOTED_IDENTIFIER OFF
GO
CREATE PROCEDURE [maintenance].[IndexFragmentationCleanup]
AS
DECLARE @reIndexRequest VARCHAR(1000)
DECLARE reIndexList CURSOR
FOR
SELECT INDEX_PROCESS
FROM (
SELECT CASE
WHEN avg_fragmentation_in_percent BETWEEN 5
AND 30
THEN 'ALTER INDEX [' + i.NAME + '] ON [' + t.NAME + '] REORGANIZE;'
WHEN avg_fragmentation_in_percent > 30
THEN 'ALTER INDEX [' + i.NAME + '] ON [' + t.NAME + '] REBUILD with(ONLINE=ON);'
END AS INDEX_PROCESS
,avg_fragmentation_in_percent
,t.NAME
FROM sys.dm_db_index_physical_stats(NULL, NULL, NULL, NULL, NULL) AS a
INNER JOIN sys.indexes AS i ON a.object_id = i.object_id
AND a.index_id = i.index_id
INNER JOIN sys.tables t ON t.object_id = i.object_id
WHERE i.NAME IS NOT NULL
) PROCESS
WHERE PROCESS.INDEX_PROCESS IS NOT NULL
ORDER BY avg_fragmentation_in_percent DESC
OPEN reIndexList
FETCH NEXT
FROM reIndexList
INTO @reIndexRequest
WHILE @@FETCH_STATUS = 0
BEGIN
BEGIN TRY
PRINT @reIndexRequest;
EXEC (@reIndexRequest);
END TRY
BEGIN CATCH
DECLARE @ErrorMessage NVARCHAR(4000);
DECLARE @ErrorSeverity INT;
DECLARE @ErrorState INT;
SELECT @ErrorMessage = 'UNABLE TO CLEAN UP INDEX WITH: ' + @reIndexRequest + ': MESSAGE GIVEN: ' + ERROR_MESSAGE()
,@ErrorSeverity = 9
,@ErrorState = ERROR_STATE();
END CATCH;
FETCH NEXT
FROM reIndexList
INTO @reIndexRequest
END
CLOSE reIndexList;
DEALLOCATE reIndexList;
RETURN 0
GO
Eu pesquisei na web e encontrei alguns bons artigos.E eu escrevi a função e o script abaixo, que é reorganizar, recriar ou reconstruir todos os índices em um banco de dados.
Primeiro você pode precisar ler este artigo para entender por que não estamos apenas recriar todos os índices.
Segundo, precisamos de uma função para criar script de criação do índice.Então, este artigo pode ajudar.Também estou compartilhando função de trabalho abaixo.
Último passo de fazer um loop while para encontrar e organizar todos os índices no banco de dados. Este vídeo é ralar exemplo para fazer isso.
Função:
create function GetIndexCreateScript(
@index_name nvarchar(100)
)
returns nvarchar(max)
as
begin
declare @Return varchar(max)
SELECT @Return = ' CREATE ' +
CASE WHEN I.is_unique = 1 THEN ' UNIQUE ' ELSE '' END +
I.type_desc COLLATE DATABASE_DEFAULT +' INDEX ' +
I.name + ' ON ' +
Schema_name(T.Schema_id)+'.'+T.name + ' ( ' +
KeyColumns + ' ) ' +
ISNULL(' INCLUDE ('+IncludedColumns+' ) ','') +
ISNULL(' WHERE '+I.Filter_definition,'') + ' WITH ( ' +
CASE WHEN I.is_padded = 1 THEN ' PAD_INDEX = ON ' ELSE ' PAD_INDEX = OFF ' END + ',' +
'FILLFACTOR = '+CONVERT(CHAR(5),CASE WHEN I.Fill_factor = 0 THEN 100 ELSE I.Fill_factor END) + ',' +
-- default value
'SORT_IN_TEMPDB = OFF ' + ',' +
CASE WHEN I.ignore_dup_key = 1 THEN ' IGNORE_DUP_KEY = ON ' ELSE ' IGNORE_DUP_KEY = OFF ' END + ',' +
CASE WHEN ST.no_recompute = 0 THEN ' STATISTICS_NORECOMPUTE = OFF ' ELSE ' STATISTICS_NORECOMPUTE = ON ' END + ',' +
-- default value
' DROP_EXISTING = ON ' + ',' +
-- default value
' ONLINE = OFF ' + ',' +
CASE WHEN I.allow_row_locks = 1 THEN ' ALLOW_ROW_LOCKS = ON ' ELSE ' ALLOW_ROW_LOCKS = OFF ' END + ',' +
CASE WHEN I.allow_page_locks = 1 THEN ' ALLOW_PAGE_LOCKS = ON ' ELSE ' ALLOW_PAGE_LOCKS = OFF ' END + ' ) ON [' +
DS.name + ' ] '
FROM sys.indexes I
JOIN sys.tables T ON T.Object_id = I.Object_id
JOIN sys.sysindexes SI ON I.Object_id = SI.id AND I.index_id = SI.indid
JOIN (SELECT * FROM (
SELECT IC2.object_id , IC2.index_id ,
STUFF((SELECT ' , ' + C.name + CASE WHEN MAX(CONVERT(INT,IC1.is_descending_key)) = 1 THEN ' DESC ' ELSE ' ASC ' END
FROM sys.index_columns IC1
JOIN Sys.columns C
ON C.object_id = IC1.object_id
AND C.column_id = IC1.column_id
AND IC1.is_included_column = 0
WHERE IC1.object_id = IC2.object_id
AND IC1.index_id = IC2.index_id
GROUP BY IC1.object_id,C.name,index_id
ORDER BY MAX(IC1.key_ordinal)
FOR XML PATH('')), 1, 2, '') KeyColumns
FROM sys.index_columns IC2
--WHERE IC2.Object_id = object_id('Person.Address') --Comment for all tables
GROUP BY IC2.object_id ,IC2.index_id) tmp3 )tmp4
ON I.object_id = tmp4.object_id AND I.Index_id = tmp4.index_id
JOIN sys.stats ST ON ST.object_id = I.object_id AND ST.stats_id = I.index_id
JOIN sys.data_spaces DS ON I.data_space_id=DS.data_space_id
JOIN sys.filegroups FG ON I.data_space_id=FG.data_space_id
LEFT JOIN (SELECT * FROM (
SELECT IC2.object_id , IC2.index_id ,
STUFF((SELECT ' , ' + C.name
FROM sys.index_columns IC1
JOIN Sys.columns C
ON C.object_id = IC1.object_id
AND C.column_id = IC1.column_id
AND IC1.is_included_column = 1
WHERE IC1.object_id = IC2.object_id
AND IC1.index_id = IC2.index_id
GROUP BY IC1.object_id,C.name,index_id
FOR XML PATH('')), 1, 2, '') IncludedColumns
FROM sys.index_columns IC2
--WHERE IC2.Object_id = object_id('Person.Address') --Comment for all tables
GROUP BY IC2.object_id ,IC2.index_id) tmp1
WHERE IncludedColumns IS NOT NULL ) tmp2
ON tmp2.object_id = I.object_id AND tmp2.index_id = I.index_id
WHERE I.is_primary_key = 0 AND I.is_unique_constraint = 0
AND I.[name] = @index_name
return @Return
end
Sql por enquanto:
declare @RebuildIndex Table(
IndexId int identity(1,1),
IndexName varchar(100),
TableSchema varchar(50),
TableName varchar(100),
Fragmentation decimal(18,2)
)
insert into @RebuildIndex (IndexName,TableSchema,TableName,Fragmentation)
SELECT
B.[name] as 'IndexName',
Schema_Name(O.[schema_id]) as 'TableSchema',
OBJECT_NAME(A.[object_id]) as 'TableName',
A.[avg_fragmentation_in_percent] Fragmentation
FROM sys.dm_db_index_physical_stats(db_id(),NULL,NULL,NULL,'LIMITED') A
INNER JOIN sys.indexes B ON A.[object_id] = B.[object_id] and A.index_id = B.index_id
INNER JOIN sys.objects O ON O.[object_id] = B.[object_id]
where B.[name] is not null and B.is_primary_key = 0 AND B.is_unique_constraint = 0 and A.[avg_fragmentation_in_percent] >= 5
--select * from @RebuildIndex
declare @begin int = 1
declare @max int
select @max = Max(IndexId) from @RebuildIndex
declare @IndexName varchar(100), @TableSchema varchar(50), @TableName varchar(100) , @Fragmentation decimal(18,2)
while @begin <= @max
begin
Select @IndexName = IndexName from @RebuildIndex where IndexId = @begin
select @TableSchema = TableSchema from @RebuildIndex where IndexId = @begin
select @TableName = TableName from @RebuildIndex where IndexId = @begin
select @Fragmentation = Fragmentation from @RebuildIndex where IndexId = @begin
declare @sql nvarchar(max)
if @Fragmentation < 31
begin
set @sql = 'ALTER INDEX ['+@IndexName+'] ON ['+@TableSchema+'].['+@TableName+'] REORGANIZE WITH ( LOB_COMPACTION = ON )'
print 'Reorganized Index ' + @IndexName + ' for ' + @TableName + ' Fragmentation was ' + convert(nvarchar(18),@Fragmentation)
end
else
begin
set @sql = (select dbo.GetIndexCreateScript(@IndexName))
if(@sql is not null)
begin
print 'Recreated Index ' + @IndexName + ' for ' + @TableName + ' Fragmentation was ' + convert(nvarchar(18),@Fragmentation)
end
else
begin
set @sql = 'ALTER INDEX ['+@IndexName+'] ON ['+@TableSchema+'].['+@TableName+'] REBUILD PARTITION = ALL WITH (ONLINE = ON)'
print 'Rebuilded Index ' + @IndexName + ' for ' + @TableName + ' Fragmentation was ' + convert(nvarchar(18),@Fragmentation)
end
end
execute(@sql)
set @begin = @begin+1
end