Question

Dans les règles SSW visant à améliorer la base de données SQL Server, vous trouverez un exemple de plan de maintenance complet de la base de données : SSW.Dans l'exemple, ils exécutent à la fois un index de réorganisation, puis un index de reconstruction, puis une mise à jour des statistiques.Est-ce que cela a un sens ?Je pensais que Reorganize Index était une version rapide mais moins efficace de Rebuild Index ?et qu'une reconstruction d'index mettrait également à jour automatiquement les statistiques (au moins sur l'index clusterisé).

Était-ce utile?

La solution

Faire un REORGANIZE et puis un REBUILD sur les mêmes index est inutile, car toute modification par le REORGANIZE serait perdu en faisant le REBUILD.

Pire encore, dans le diagramme du plan de maintenance de SSW, il effectue une SHRINK premièrement, qui fragmente les index comme effet secondaire de la façon dont il libère de l'espace.Puis le REBUILD alloue à nouveau plus d'espace aux fichiers de base de données comme espace de travail pendant le REBUILD opération.

  • REORGANIZE est une opération en ligne qui défragmente les pages feuilles d'un index clusterisé ou non clusterisé page par page en utilisant peu d'espace de travail supplémentaire.

  • REBUILD est une opération en ligne dans les éditions Enterprise, hors ligne dans d'autres éditions et utilise à nouveau autant d'espace de travail supplémentaire que la taille de l'index.Il crée une nouvelle copie de l'index puis supprime l'ancienne, éliminant ainsi la fragmentation.Les statistiques sont recalculées par défaut dans le cadre de cette opération, mais cela peut être désactivé.

Voir Réorganisation et reconstruction des index pour plus d'informations.

Ne pas utiliser SHRINK sauf avec le TRUNCATEONLY option et même dans ce cas, si le fichier grossit à nouveau, vous devriez réfléchir sérieusement à la question de savoir si cela est nécessaire :

sqlservercentral_SHRINKFILE

Autres conseils

La réorganisation et la reconstruction sont des choses différentes.

Réorganiser:c'est une défragmentation pour les index.Prend le(s) index existant(s) et défragmente les pages existantes.Cependant si les pages ne sont pas contiguës, elles restent comme avant.Seul le contenu des pages change.

Reconstruire:en fait, il supprime l'index et le reconstruit à partir de zéro.Cela signifie que vous obtiendrez un tout nouvel index, avec des pages défragmentées et contiguës.

De plus, avec la reconstruction, vous pouvez modifier le partitionnement ou les groupes de fichiers, mais avec la réorganisation, vous pouvez défragmenter non seulement l'ensemble de l'index, mais également une seule partition de l'index.

La mise à jour des statistiques est automatique sur les index clusterisés, mais pas sur les index non clusterisés.

Avant d’envisager la maintenance des index, il est important de répondre à deux questions principales :

  1. Quel est le degré de fragmentation ?
  2. Quelle est l’action appropriée ?Réorganiser ou reconstruire ?

Comme décrit dans cet article http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/, et pour vous aider à déterminer si vous devez effectuer une reconstruction ou une réorganisation d'index, veuillez comprendre ce qui suit :

  • La réorganisation de l'index est un processus dans lequel SQL Server parcourt l'index existant et le nettoie.La reconstruction de l'index est un processus intensif dans lequel l'index est supprimé puis recréé à partir de zéro avec une structure entièrement nouvelle, exempte de fragments empilés et de pages vides.

  • Alors que la réorganisation de l'index est une pure opération de nettoyage qui laisse l'état du système tel quel sans verrouiller les tables et vues affectées, le processus de reconstruction verrouille la table affectée pendant toute la période de reconstruction, ce qui peut entraîner de longs temps d'arrêt qui ne pourraient pas être acceptables dans certains environnements.En gardant cela à l’esprit, il est clair que la reconstruction de l’index est un processus avec une solution « plus solide », mais il a un prix : d’éventuels verrous longs sur les tables indexées concernées.

D’un autre côté, la réorganisation de l’index est un processus « léger » qui résoudra la fragmentation de manière moins efficace – puisque l’index nettoyé sera toujours deuxième après le nouveau entièrement créé à partir de zéro.Mais la réorganisation de l'index est bien meilleure du point de vue de l'efficacité, car elle ne verrouille pas la table indexée affectée au cours de l'opération.

L'article mentionné ci-dessus explique également comment réorganiser et reconstruire les index à l'aide de SSMS, T-SQL (pour réorganiser/reconstruire les index dans une table) et un outil tiers appelé ApexSQL Backup.

Lors de la réorganisation d'un index, si l'index est réparti sur deux fichiers physiques ou plus, les données ne seront défragmentées que dans le fichier de données.Les pages ne sont pas déplacées d'un fichier de données à un autre.

Lorsque l'index est dans un seul fichier, la réorganisation et la réindexation auront le même résultat final.

Parfois, la réorganisation sera plus rapide, et parfois la réindexation sera plus rapide en fonction du degré de fragmentation de l'index.Moins l'index est fragmenté, une réorganisation sera plus rapide, plus fragmentée, plus la réorganisation sera lente, mais plus une réindexation sera rapide.

Exactement quoi Biri dit.Voici comment réindexer une base de données entière :

EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')"

J'utilise ce 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

et créez un travail qui exécute ce SP chaque semaine.

Mieux encore, c'est :

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX'

ou

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE'

Mes deux centimes...Cette méthode suit les spécifications décrites sur 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

J'ai fait des recherches sur le Web et j'ai trouvé de bons articles.À l'époque, j'ai écrit la fonction et le script ci-dessous qui permettent de réorganiser, recréer ou reconstruire tous les index d'une base de données.

Vous devrez peut-être d'abord lire Cet article pour comprendre pourquoi nous ne recréons pas simplement tous les index.

Deuxièmement, nous avons besoin d'une fonction pour créer un script de création pour l'index.Donc Cet article peut aider.Je partage également la fonction de travail ci-dessous.

Dernière étape créant une boucle while pour rechercher et organiser tous les index de la base de données. Cette vidéo est un excellent exemple pour faire cela.

Fonction:

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 pendant un moment :

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
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top