Riorganizzare vs indice di Indice di Ricostruzione nel piano di Manutenzione Sql Server
-
08-06-2019 - |
Domanda
In SSW regole meglio di Database di SQL Server c'è un esempio di un completo piano di manutenzione database: SW.Nell'esempio vengono eseguiti sia a Riorganizzare Indice e quindi un Indice di Ricostruzione e quindi Aggiornare le Statistiche.C'è qualche punto di questo?Ho pensato di Riorganizzare Indice è stato un veloce ma meno efficace versione di Ricostruire l'Indice?e che la ricostruzione di un indice sarebbe anche aggiornare le statistiche automaticamente (indice cluster almeno).
Soluzione
Facendo un REORGANIZE
e poi un REBUILD
sulla stessa indici è inutile, come tutte le modifiche da REORGANIZE
sarebbe perso facendo il REBUILD
.
Peggio è che nel piano di manutenzione schema da SSW, esegue una SHRINK
in primo luogo, che i frammenti di indici come un effetto collaterale di modo che rilascia spazio.Quindi il REBUILD
alloca lo spazio per il file di database di nuovo come spazio di lavoro durante il REBUILD
operazione.
REORGANIZE
è un'operazione in linea che consente di deframmentare pagine foglia in un cluster o un indice non cluster pagina per pagina utilizzando piccolo extra spazio di lavoro.REBUILD
è un'operazione in linea con le edizioni Enterprise, collegato in altre edizioni, e utilizza una quantità supplementare di spazio di lavoro di nuovo come la dimensione dell'indice.Crea una nuova copia dell'indice e quindi elimina la vecchia, così sbarazzarsi di frammentazione.Vengono ricalcolate le statistiche di default come parte di questa operazione, ma che può essere disattivato.
Vedere Riorganizzazione e Ricostruzione degli Indici per ulteriori informazioni.
Non utilizzare SHRINK
tranne che con il TRUNCATEONLY
opzione e poi anche se il file crescerà di nuovo, allora si dovrebbe riflettere se è necessario:
Altri suggerimenti
La riorganizzazione e ricostruzione sono cose diverse.
Riorganizzare:si tratta di un defrag per gli indici.Prende l'indice esistente(es) e deframmenta pagine esistenti.Tuttavia, se le pagine non sono in un modo contiguo, si rimane come prima.Solo il contenuto delle pagine stanno cambiando.
Ricostruzione:in realtà non si scende l'indice e lo ricrea da zero.Significa che si otterrà un completamente nuovo indice, con deframmentato e pagine contigue.
Inoltre con la ricostruzione è possibile modificare il partizionamento o gruppi di file, ma con la riorganizzazione si può defrag non solo l'intero indice, ma anche solo una partizione dell'indice.
L'aggiornamento delle statistiche è automatica indici cluster, ma non non cluster quelli.
Prima di considerare la manutenzione di indici, è importante rispondere a due domande fondamentali:
- Qual è il grado di frammentazione?
- Che cosa è l'azione appropriata?Riorganizzare o ricostruire?
Come descritto in questo articolo http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/, e per aiutare a determinare se si dovrebbe eseguire la ricostruzione di un indice o di un indice riorganizzazione, la preghiamo di comprendere i seguenti:
Indice di riorganizzazione è un processo in cui SQL Server passa indice esistente e pulisce.Ricostruzione di un indice è un pesante processo dove l'indice viene eliminato e ricreato da zero, completamente nuova struttura, libero da tutti ammucchiati frammenti e vuoto-spazio pagine.
Mentre l'indice di riorganizzazione è una pura operazione di pulizia che lascia dello stato del sistema, in quanto priva di blocco interessato tabelle e viste, il processo di ricostruzione serrature tabella interessata, per l'intero periodo di rigenerazione, il che può comportare lunghi tempi di inattività che non può essere accettabile in alcuni ambienti.Con questo in mente, è chiaro che la ricostruzione dell'indice, è un processo con più "forti" soluzione, ma si tratta di un prezzo – possibile serrature a lungo interessati tabelle indicizzate.
Dall'altro lato, indice di riorganizzazione è un ‘leggero’ processo per risolvere la frammentazione in un meno efficace – dal puliti indice sarà sempre seconda al nuovo completamente fatto da zero.Ma la riorganizzazione indice è molto meglio dal punto di vista di efficienza, dal momento che non si blocca interessato tabella indicizzata durante il corso dell'operazione.
L'articolo di cui sopra spiega anche come riorganizzare e ricostruire gli indici utilizzando sql server management studio, T-SQL (per riorganizzare/ricostruire gli indici di una tabella) e un 3rd party strumento chiamato ApexSQL di Backup.
Quando si fa una riorganizzazione di un indice, l'indice è disposta su due o più file fisici i dati potranno essere deframmentato interno del file di dati.Le pagine non vengono spostati da un file di dati di un altro.
Quando l'indice è in un unico file la riorganizzazione e il reindex avrà lo stesso risultato finale.
Alcune volte la riorganizzazione sarà più veloce, e alcune volte il reindex sarà più veloce a seconda del grado di frammentazione dell'indice.Meno frammentato l'indice quindi di una riorganizzazione sarà più veloce, più frammentato, più lenta è la riorganizzazione sarà, ma il più veloce di un reindex sarà.
Esattamente quello che Biri detto.Ecco come vorrei il reindex di un intero database:
EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')"
Io Uso questo 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 creare Un Lavoro che comportano l'esecuzione di questo SP ogni settimana.
Ancora meglio è:
EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX'
o
EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE'
I miei due centesimi...Questo metodo segue le specifiche riportate sull'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
Ho cercato sul web e ho trovato alcuni ottimi articoli.Alla fine ho scritto la funzione di script e al di sotto della quale è riorganizzare, ricreare e ricostruire tutti gli indici in un database.
Il primo potrebbe essere necessario leggere questo articolo per capire perché non siamo solo di ricreare tutti gli indici.
Secondo, abbiamo bisogno di una funzione per creare script di creazione dell'indice.Così questo articolo può essere di aiuto.Anche io sto condivisione di lavoro la funzione qui sotto.
Ultima fase di un ciclo while per trovare e organizzare tutti gli indici del database. Questo video è grata esempio per fare questo.
Funzione:
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 per un po':
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