2008-08-11 9 views
32

In den SSW-Regeln zur besseren SQL Server-Datenbank gibt es ein Beispiel für eine vollständige Datenbank Wartungsplan: SSW. In diesem Beispiel führen sie sowohl einen Reorganisieren-Index als auch einen Neuerstellungsindex aus und aktualisieren dann die Statistik. Hat das irgendeinen Sinn? Ich dachte, Reorganize Index wäre eine schnelle, aber weniger effektive Version von Rebuild Index? und dass eine Indexrekonstruktion auch die Statistiken automatisch aktualisiert (zumindest im Clustered-Index).Reorganisieren Sie Index vs Rebuild-Index in Sql Server Wartungsplan

Antwort

16

ein REORGANIZE tun und dann eine REBUILD auf den gleichen Indizes ist sinnlos, da alle Änderungen durch die REORGANIZE würde, indem Sie die REBUILD verloren.

Schlimmer als das ist, dass im Wartungsplan Diagramm von SSW, führt es zuerst eine SHRINK, die die Indizes als Nebeneffekt der Art, wie es Raum freigibt. Dann ordnet der REBUILD den Datenbankdateien wieder mehr Speicherplatz als Arbeitsspeicher während der Operation REBUILD zu.

  • REORGANIZE ist ein Online-Betrieb, die Blattseiten in einer gruppierten oder nicht gruppierten Index Seite für Seite mit wenig zusätzlichen Arbeitsraum defragmentiert.

  • REBUILD ist ein Online-Vorgang in Enterprise-Editionen, der in anderen Editionen offline ist, und verwendet genauso viel zusätzlichen Arbeitsraum wie die Indexgröße. Es erstellt eine neue Kopie des Index und löscht dann die alte, wodurch die Fragmentierung beseitigt wird. Im Rahmen dieser Operation werden standardmäßig Statistiken neu berechnet, die jedoch deaktiviert werden können.

Weitere Informationen finden Sie unter Reorganizing and Rebuilding Indexes.

nicht SHRINK außer mit der TRUNCATEONLY Option und auch dann verwenden Sie, wenn die Datei wieder wachsen wird, dann sollten Sie denken, hart, ob es notwendig ist:

sqlservercentral_SHRINKFILE

+0

Es ist erstaunlich, wie viele Online- "Behörden" völlig inkorrekt und irreführend sind, d. H. Vorzuschlagen, dass Sie eine Datenbank verkleinern sollten !! –

30

Die Reorganisation und Neuerstellung sind verschiedene Dinge.

Reorganisieren: Es ist eine Defragmentierung für Indizes. Nimmt die vorhandenen Indexe und defragmentiert die vorhandenen Seiten. Wenn die Seiten jedoch nicht zusammenhängend sind, bleiben sie wie zuvor. Nur der Inhalt der Seiten ändert sich.

Rebuild: Tatsächlich wird der Index gelöscht und von Grund auf neu erstellt. Dies bedeutet, dass Sie einen vollständig neuen Index mit defragmentierten und zusammenhängenden Seiten erhalten.

Darüber hinaus können Sie mit Rebuild Partitionierung oder Dateigruppen ändern, aber mit Reorganisation können Sie nicht nur den gesamten Index, sondern auch nur eine Partition des Index defragmentieren.

Die Aktualisierungsstatistik ist bei Clustered-Indizes automatisch, nicht jedoch bei Nicht-Clustered-Indizes.

+1

Richtig, aber ist es sinnvoll, sowohl eine Reorganisation als auch eine Neuerstellung im selben Maintenance Sub Plan durchzuführen? – codeulike

+2

Eigentlich nach Online-Dokumentation http://msdn.microsoft.com/en-us/library/ms189858.aspx Reorg DOES reorganisieren die Seiten, um sie physisch zusammenhängend zu machen. Hier ist das genaue Zitat: "Reorganisieren eines Index defragmentiert die Blattebene von geclusterten und nicht gruppierten Indizes für Tabellen und Sichten durch physikalische Neuordnung der Seiten auf Blattebene, um der logischen Reihenfolge (von links nach rechts) der Blattknoten zu entsprechen Damit wird die Index-Scan-Leistung verbessert. Der Index wird innerhalb der ihm zugewiesenen vorhandenen Seiten neu organisiert, es werden keine neuen Seiten zugewiesen. " –

+2

@ MichaelK.Campbell: Ihr Zitat wurde ein wenig aus dem Zusammenhang genommen. Während ReOrg die Seiten neu anordnet, werden sie nur auf der untersten Ebene neu angeordnet, auf der Knoten der mittleren Ebene auf sie zeigen. Nach einem ReOrg sind nicht alle Seiten über den gesamten Index hinweg zusammenhängend. Hier ist eine bessere Erklärung: http://dba.stackexchange.com/a/36817/6816 – MikeTeeVee

7

Genau was Biri sagte. Hier ist, wie ich eine ganze Datenbank indizieren:

10

Wenn ein Umschichten eines Index zu tun, wenn der Index über zwei oder mehr physische Dateien verteilt die Daten nur innerhalb der Datendatei defragmentiert werden. Seiten werden nicht von einer Datendatei in eine andere verschoben.

Wenn sich der Index in einer einzigen Datei befindet, haben Reorg und Reindex dasselbe Endergebnis.

Manchmal ist die Reorganisation schneller, und manchmal wird der Neuindex schneller, je nachdem wie fragmentiert der Index ist. Je weniger fragmentiert der Index ist, desto schneller wird eine Reorganisation, je fragmentierter die Reorganisation ist, desto schneller wird ein Re-Index.

3

Noch besser ist:

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX' 

oder

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE' 
5

Ich benutze dieses 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 

und einen Job erstellen, die diese SP jede Woche auszuführen.

0

Meine zwei Cent ... folgt dieser Methode wird die Spezifikation auf Tech-Netz skizziert: 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 
0

ich auf Web recherchiert und einige gute Artikel gefunden. Am und ich schrieb die Funktion und das Skript, unter dem reorganisiert wird, neu erstellen oder alle Indizes in einer Datenbank neu erstellen.

Zuerst müssen Sie möglicherweise this article lesen, um zu verstehen, warum wir nicht nur alle Indizes neu erstellen.

Zweitens brauchen wir eine Funktion zum Erstellen von create-Skript für den Index. So kann this article helfen. Auch ich teile Arbeitsfunktion unten.

Letzter Schritt, der eine while-Schleife erstellt, um alle Indizes in der Datenbank zu finden und zu organisieren. This video ist ein Beispiel, um dies zu machen.

Funktion:

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 für während:

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 ['[email protected]+'] ON ['[email protected]+'].['[email protected]+'] 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 ['[email protected]+'] ON ['[email protected]+'].['[email protected]+'] 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 
21

Vor der Wartung der Indizes unter Berücksichtigung, ist es wichtig, zwei Fragen zu beantworten:

  1. Was ist der Grad der Zersplitterung?
  2. Was ist die geeignete Maßnahme? Reorganisieren oder neu aufbauen?

Wie http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/ in diesem Artikel beschrieben wird, und Ihnen helfen, festzustellen, ob Sie Index neu erstellen oder Indexreorganisation durchführen sollten, verstehen Sie bitte folgendes:

  • Index Reorganisation ist ein Prozess, in dem der SQL Server geht durch vorhandenen Index und räumt auf. Indexrekonstruktion ist ein Heavy-Duty-Prozess, bei dem der Index gelöscht und dann von Grund auf neu erstellt wird, mit einer völlig neuen Struktur, frei von allen gestapelten Fragmenten und Leerseiten.Wenn die Indexreorganisation eine reine Bereinigungsoperation ist, die den Systemstatus unverändert belässt, ohne betroffene Tabellen und Sichten zu sperren, sperrt der Neuerstellungsprozess die betroffene Tabelle für die gesamte Neuerstellungsperiode, was zu langen Ausfallzeiten führen kann könnte in einigen Umgebungen nicht akzeptabel sein. In diesem Sinne ist es klar, dass der Index-Umbau ein Prozess mit "stärkerer" Lösung ist, aber es kommt mit einem Preis - mögliche lange Sperren auf betroffenen indizierten Tabellen.

Auf der anderen Seite ist ein Indexreorganisation ‚leichte‘ Prozess, der die Fragmentierung in einem weniger effektiven Weise lösen - da gereinigt Index von Grund auf aus vollständig in die neue zweite immer sein wird. Das Reorganisieren des Index ist jedoch vom Standpunkt der Effizienz aus viel besser, da die betroffene indizierte Tabelle während des Betriebs nicht gesperrt wird.

Der oben erwähnte Artikel erläutert auch, wie Indizes mithilfe von SSMS, T-SQL (zum Reorganisieren/Neuanlegen von Indizes in einer Tabelle) und einem Tool von Drittanbietern namens ApexSQL Backup neu organisiert und neu erstellt werden.