إعادة تنظيم الفهرس مقابل إعادة بناء الفهرس في خطة صيانة خادم Sql

StackOverflow https://stackoverflow.com/questions/7579

سؤال

يوجد في قواعد SSW لتحسين قاعدة بيانات SQL Server مثال لخطة صيانة قاعدة البيانات الكاملة: SSW.في المثال، يقومون بتشغيل كل من فهرس إعادة التنظيم ثم فهرس إعادة البناء ثم تحديث الإحصائيات.هل هناك أي نقطة لهذا؟اعتقدت أن إعادة تنظيم الفهرس كان إصدارًا سريعًا ولكنه أقل فعالية من إعادة بناء الفهرس؟وأن إعادة بناء الفهرس ستؤدي أيضًا إلى تحديث الإحصائيات تلقائيًا (على الفهرس المجمع على الأقل).

هل كانت مفيدة؟

المحلول

القيام REORGANIZE ومن ثم أ REBUILD على نفس الفهارس لا طائل منه، كما أن أي تغييرات من قبل REORGANIZE سوف تضيع عن طريق القيام REBUILD.

والأسوأ من ذلك هو أنه في مخطط خطة الصيانة من SSW، فإنه ينفذ SHRINK أولاً، الذي يؤدي إلى تجزئة الفهارس كأثر جانبي للطريقة التي يحرر بها المساحة.ثم REBUILD يخصص مساحة أكبر لملفات قاعدة البيانات مرة أخرى كمساحة عمل أثناء REBUILD عملية.

  • REORGANIZE هي عملية عبر الإنترنت تعمل على إلغاء تجزئة الصفحات الورقية في صفحة فهرس مجمعة أو غير مجمعة تلو الأخرى باستخدام مساحة عمل إضافية صغيرة.

  • REBUILD هي عملية عبر الإنترنت في إصدارات Enterprise، وغير متصلة بالإنترنت في الإصدارات الأخرى، وتستخدم مساحة عمل إضافية بقدر حجم الفهرس مرة أخرى.يقوم بإنشاء نسخة جديدة من الفهرس ثم يسقط النسخة القديمة، وبالتالي التخلص من التجزئة.تتم إعادة حساب الإحصائيات بشكل افتراضي كجزء من هذه العملية، ولكن يمكن تعطيل ذلك.

يرى إعادة تنظيم وإعادة بناء الفهارس للمزيد من المعلومات.

لا تستخدم SHRINK إلا مع TRUNCATEONLY الخيار، وحتى إذا كان الملف سينمو مرة أخرى، فيجب عليك التفكير مليًا فيما إذا كان ذلك ضروريًا أم لا:

sqlservercentral_SHRINKFILE

نصائح أخرى

إعادة التنظيم وإعادة البناء أمران مختلفان.

إعادة التنظيم:إنه إلغاء تجزئة للفهارس.يأخذ الفهرس (الفهارس) الموجودة ويقوم بإلغاء تجزئة الصفحات الموجودة.ومع ذلك، إذا لم تكن الصفحات متجاورة، فإنها تظل كما كانت من قبل.فقط محتوى الصفحات هو الذي يتغير.

إعادة بناء:في الواقع يقوم بإسقاط الفهرس وإعادة بنائه من الصفر.هذا يعني أنك ستحصل على فهرس جديد تمامًا، بصفحات مجزأة ومتجاورة.

علاوة على ذلك، من خلال إعادة البناء، يمكنك تغيير التقسيم أو مجموعات الملفات، ولكن مع إعادة التنظيم، لا يمكنك إلغاء تجزئة الفهرس بأكمله فحسب، بل أيضًا قسم واحد فقط من الفهرس.

تكون إحصائيات التحديث تلقائية على الفهارس المجمعة، ولكن ليس على الفهارس غير المجمعة.

قبل النظر في صيانة الفهارس، من المهم الإجابة على سؤالين رئيسيين:

  1. ما هي درجة التجزئة؟
  2. ما هو الإجراء المناسب؟إعادة التنظيم أم البناء؟

كما هو موضح في هذه المقالة http://solutioncenter.apexsql.com/why-when-and-how-to-rebuild-and-reorganize-sql-server-indexes/, ولمساعدتك في تحديد ما إذا كان يجب عليك إجراء إعادة بناء الفهرس أو إعادة تنظيم الفهرس، يرجى فهم ما يلي:

  • إعادة تنظيم الفهرس هي عملية يمر فيها SQL Server عبر الفهرس الموجود ويقوم بتنظيفه.تعد إعادة بناء الفهرس عملية شاقة حيث يتم حذف الفهرس ثم إعادة إنشائه من البداية ببنية جديدة تمامًا، وخالية من جميع الأجزاء المتراكمة والصفحات ذات المساحات الفارغة.

  • على الرغم من أن إعادة تنظيم الفهرس هي عملية تنظيف خالصة تترك حالة النظام كما هي دون تأمين الجداول وطرق العرض المتأثرة، فإن عملية إعادة البناء تقوم بتأمين الجدول المتأثر طوال فترة إعادة البناء بأكملها، مما قد يؤدي إلى فترات توقف طويلة قد لا تكون مقبولة في بعض البيئات.مع أخذ ذلك في الاعتبار، من الواضح أن إعادة بناء الفهرس هي عملية ذات حل "أقوى"، ولكنها تأتي مع سعر - عمليات تأمين طويلة محتملة على الجداول المفهرسة المتأثرة.

على الجانب الآخر، تعد إعادة تنظيم الفهرس عملية "خفيفة الوزن" ستحل التجزئة بطريقة أقل فعالية - نظرًا لأن الفهرس المنظف سيكون دائمًا في المرتبة الثانية بعد الفهرس الجديد المصنوع بالكامل من الصفر.لكن إعادة تنظيم الفهرس أفضل بكثير من وجهة نظر الكفاءة، حيث أنها لا تقفل الجدول المفهرس المتأثر أثناء سير العملية.

تشرح المقالة المذكورة أعلاه أيضًا كيفية إعادة تنظيم الفهارس وإعادة بنائها باستخدام SSMS وT-SQL (لإعادة تنظيم/إعادة بناء الفهارس في جدول) وأداة تابعة لجهة خارجية تسمى ApexSQL Backup.

عند القيام بإعادة تنظيم الفهرس، إذا كان الفهرس منتشرًا عبر ملفين فعليين أو أكثر، فسيتم إلغاء تجزئة البيانات داخل ملف البيانات فقط.لا يتم نقل الصفحات من ملف بيانات إلى آخر.

عندما يكون الفهرس في ملف واحد، فإن إعادة التنظيم وإعادة الفهرسة سيكون لهما نفس النتيجة النهائية.

في بعض الأحيان، ستكون عملية إعادة التنظيم أسرع، وفي بعض الأحيان ستكون إعادة الفهرسة أسرع اعتمادًا على مدى تجزئة الفهرس.كلما كان الفهرس أقل تجزئة، أصبحت عملية إعادة التنظيم أسرع، وكلما كانت عملية إعادة التنظيم أكثر تجزئة، ولكن كلما كانت عملية إعادة الفهرسة أسرع.

ماذا بالضبط بيري قال.إليك كيفية إعادة فهرسة قاعدة بيانات بأكملها:

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

أنا استخدم هذا 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

وقم بإنشاء وظيفة واحدة تنفذ هذه الخطة SP كل أسبوع.

والأفضل من ذلك هو:

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REINDEX'

أو

EXEC sp_MSforeachtable 'ALTER INDEX ALL ON ? REORGANIZE'

سنتى...تتبع هذه الطريقة المواصفات الموضحة على شبكة التكنولوجيا: 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

لقد بحثت على شبكة الإنترنت ووجدت بعض المقالات الجيدة.في و كتبت الوظيفة والبرنامج النصي أدناه وهو إعادة تنظيم أو إعادة إنشاء أو إعادة بناء جميع الفهارس في قاعدة البيانات.

أولا قد تحتاج إلى القراءة هذا المقال لفهم سبب عدم قيامنا بإعادة إنشاء جميع الفهارس فقط.

ثانيًا، نحتاج إلى وظيفة لإنشاء برنامج نصي للفهرس.لذا هذا المقال قد يساعد.كما أنني أشارك وظيفة العمل أدناه.

الخطوة الأخيرة هي إنشاء حلقة زمنية للعثور على كافة الفهارس وتنظيمها في قاعدة البيانات. هذا الفيديو هو مثال صر للقيام بذلك.

وظيفة:

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
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top