Реорганизация индекса по сравнению с перестроением индекса в плане обслуживания сервера Sql

В правилах SSW для улучшения базы данных SQL Server приведен пример полного плана обслуживания базы данных: SSW. В примере они запускают как индекс реорганизации, так и индекс перестроения, а затем обновляют статистику. Есть ли в этом смысл? Я думал, что Reorganize Index — это быстрая, но менее эффективная версия Rebuild Index? и что перестроение индекса также автоматически обновит статистику (по крайней мере, для кластеризованного индекса).


person BTB    schedule 11.08.2008    source источник


Ответы (9)


Выполнение REORGANIZE, а затем REBUILD для одних и тех же индексов бессмысленно, так как любые изменения REORGANIZE будут потеряны при выполнении REBUILD.

Хуже того, на диаграмме плана обслуживания из SSW он сначала выполняет SHRINK, который фрагментирует индексы как побочный эффект того, как он освобождает пространство. Затем REBUILD снова выделяет больше места для файлов базы данных в качестве рабочего пространства во время операции REBUILD.

  • REORGANIZE — это онлайн-операция, которая дефрагментирует конечные страницы в кластеризованном или некластеризованном индексе постранично, используя небольшое дополнительное рабочее пространство.

  • REBUILD — это онлайн-операция в редакциях Enterprise, автономная операция в других редакциях, и она снова использует столько же дополнительного рабочего пространства, сколько и размер индекса. Он создает новую копию индекса, а затем удаляет старую, тем самым избавляясь от фрагментации. Статистика пересчитывается по умолчанию как часть этой операции, но ее можно отключить.

Дополнительные сведения см. в разделе Реорганизация и перестроение индексов.

Не используйте SHRINK, кроме как с опцией TRUNCATEONLY, и даже в этом случае, если файл снова будет расти, вам следует хорошенько подумать, нужно ли это:

sqlservercentral_SHRINKFILE

person Llew    schedule 24.02.2012
comment
Удивительно, как много онлайн-авторитетов совершенно неверны и вводят в заблуждение, т. е. предлагают вам уменьшить базу данных!! - person Nick.McDermaid; 13.07.2014
comment
Не правда. Если вы сначала «реорганизуете», вы потенциально сжимаете страницы данных. Это оперативная операция во всех версиях SQL Server, и ее можно выполнять постепенно. Полная перестройка требует больше ресурсов. Выполняя сначала реорганизацию и постепенное сжатие страниц данных, вы уменьшите количество операций ввода-вывода, необходимых для последующей перестройки, поскольку она будет считывать меньше страниц данных, что приведет к меньшему количеству операций ввода-вывода и использованию меньшего объема памяти. Это было бы измеримо. Сказать, что реорганизация не влияет на последующую перестройку, это нонсенс, ИМО. - person Triynko; 15.07.2020

Реорганизация и перестройка - разные вещи.

Реорганизация: это дефрагментация индексов. Берет существующие индексы и дефрагментирует существующие страницы. Однако, если страницы не являются смежными, они остаются прежними. Меняется только содержимое страниц.

Перестроить: на самом деле он удаляет индекс и перестраивает его с нуля. Это означает, что вы получите совершенно новый индекс с дефрагментированными и смежными страницами.

Более того, с помощью перестроения вы можете изменить разбиение или группы файлов, а с помощью реорганизации вы можете дефрагментировать не только весь индекс, но и только один раздел индекса.

Обновление статистики выполняется автоматически для кластеризованных индексов, но не для некластеризованных.

person Biri    schedule 11.08.2008
comment
Верно, но есть ли смысл включать в один и тот же подплан обслуживания и реорганизацию, и перестроение? - person codeulike; 22.08.2011
comment
На самом деле, согласно Books Online msdn.microsoft.com/en-us/library/ ms189858.aspx Reorg реорганизует страницы, чтобы сделать их физически смежными. Вот точная цитата: Реорганизация индекса дефрагментирует конечный уровень кластеризованных и некластеризованных индексов для таблиц и представлений, физически переупорядочивая конечные страницы в соответствии с логическим порядком (слева направо) конечных узлов. Порядок страниц повышает производительность сканирования индекса. Индекс реорганизуется в пределах выделенных ему существующих страниц; новые страницы не выделяются. - person Michael K. Campbell; 02.03.2012
comment
@MichaelK.Campbell: Ваша цитата была немного вырвана из контекста. Хотя ReOrg переупорядочивает страницы, он переупорядочивает их ТОЛЬКО на самом низком уровне, на который на них указывают узлы промежуточного уровня. После ReOrg не гарантируется, что все страницы во всем индексе будут непрерывными. Вот лучшее объяснение: dba.stackexchange.com/a/36817/6816 - person MikeTeeVee; 20.01.2014

Прежде чем рассматривать обслуживание индексов, важно ответить на два основных вопроса:

  1. Какова степень фрагментации?
  2. Каково соответствующее действие? Реорганизовать или перестроить?

Как описано в этой статье http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/ и помочь вам определить, следует ли выполнять перестроение или реорганизацию индекса. , пожалуйста, поймите следующее:

  • Реорганизация индекса — это процесс, в котором SQL Server просматривает существующий индекс и очищает его. Перестроение индекса — это сложный процесс, при котором индекс удаляется, а затем воссоздается с нуля с совершенно новой структурой, свободной от всех нагроможденных фрагментов и пустых страниц.

  • В то время как реорганизация индекса является чистой операцией очистки, которая оставляет состояние системы таким, какое оно есть, без блокировки затронутых таблиц и представлений, процесс перестроения блокирует затронутую таблицу на весь период перестроения, что может привести к длительным простоям, которые недопустимы в некоторые среды. Имея это в виду, становится ясно, что перестроение индекса — это процесс с более «сильным» решением, но за него приходится платить — возможные длительные блокировки затронутых индексированных таблиц.

С другой стороны, реорганизация индекса — это «легкий» процесс, который решит проблему фрагментации менее эффективным способом, поскольку очищенный индекс всегда будет вторым после нового, полностью созданного с нуля. Но реорганизация индекса намного лучше с точки зрения эффективности, так как она не блокирует затронутую индексированную таблицу во время работы.

В упомянутой выше статье также объясняется, как реорганизовать и перестроить индексы с помощью SSMS, T-SQL (для реорганизации/перестроения индексов в таблице) и стороннего инструмента под названием ApexSQL Backup.

person Alex Kirilov    schedule 27.01.2016
comment
при перестроении индекса необходимо обновлять статистику таблицы? особенно если есть некластеризованный индекс? - person Moudiz; 03.04.2018

При реорганизации индекса, если индекс распределен по двум или более физическим файлам, данные будут дефрагментированы только в файле данных. Страницы не перемещаются из одного файла данных в другой.

Когда индекс находится в одном файле, реорганизация и переиндексация будут иметь одинаковый конечный результат.

В некоторых случаях реорганизация будет выполняться быстрее, а в некоторых случаях переиндексация будет выполняться быстрее в зависимости от того, насколько фрагментирован индекс. Чем менее фрагментирован индекс, тем быстрее будет реорганизация, чем более фрагментирована, тем медленнее будет реорганизация, но тем быстрее будет переиндексация.

person mrdenny    schedule 03.09.2008

Что именно Biri сказал. Вот как я бы переиндексировал всю базу данных:

EXEC [sp_MSforeachtable] @command1="RAISERROR('DBCC DBREINDEX(''?'') ...',10,1) WITH NOWAIT DBCC DBREINDEX('?')"
person GateKiller    schedule 11.08.2008
comment
Предпочтительный способ теперь не использовать Alter Index - docs.microsoft.com/en-us/sql/t-sql/statements/ - person Rohit Vipin Mathews; 21.11.2019

Я использую этот СП

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

и создайте одно задание, которое будет выполнять этот SP каждую неделю.

person Ardalan Shahgholi    schedule 25.09.2013

Еще лучше:

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX'

or

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE'
person Chris KL    schedule 26.06.2013

Я исследовал в Интернете и нашел несколько хороших статей. В конце я написал функцию и сценарий ниже, которые реорганизуют, воссоздают или перестраивают все индексы в базе данных.

Сначала вам может понадобиться прочитать эту статью, чтобы понять, почему мы не просто воссоздаем все индексы.

Во-вторых, нам нужна функция для создания сценария создания файла index. Так что эта статья может помочь. Также я делюсь рабочей функцией ниже.

Последний шаг — создание цикла while для поиска и организации всех индексов в базе данных. Это видео — отличный тому пример.

Функция:

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 на время:

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
person vast    schedule 28.01.2015

Мои два цента... Этот метод соответствует спецификации, описанной в технической сети: 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
person mcfea    schedule 03.10.2014
comment
это отлично, но это приводит к дублированию, а это означает, что реорганизация или перестроение могут происходить несколько раз, чего следует избегать. - person smoore4; 26.03.2021