إعادة تنظيم الفهرس مقابل إعادة بناء الفهرس في خطة صيانة خادم Sql
-
08-06-2019 - |
سؤال
يوجد في قواعد SSW لتحسين قاعدة بيانات SQL Server مثال لخطة صيانة قاعدة البيانات الكاملة: SSW.في المثال، يقومون بتشغيل كل من فهرس إعادة التنظيم ثم فهرس إعادة البناء ثم تحديث الإحصائيات.هل هناك أي نقطة لهذا؟اعتقدت أن إعادة تنظيم الفهرس كان إصدارًا سريعًا ولكنه أقل فعالية من إعادة بناء الفهرس؟وأن إعادة بناء الفهرس ستؤدي أيضًا إلى تحديث الإحصائيات تلقائيًا (على الفهرس المجمع على الأقل).
المحلول
القيام REORGANIZE
ومن ثم أ REBUILD
على نفس الفهارس لا طائل منه، كما أن أي تغييرات من قبل REORGANIZE
سوف تضيع عن طريق القيام REBUILD
.
والأسوأ من ذلك هو أنه في مخطط خطة الصيانة من SSW، فإنه ينفذ SHRINK
أولاً، الذي يؤدي إلى تجزئة الفهارس كأثر جانبي للطريقة التي يحرر بها المساحة.ثم REBUILD
يخصص مساحة أكبر لملفات قاعدة البيانات مرة أخرى كمساحة عمل أثناء REBUILD
عملية.
REORGANIZE
هي عملية عبر الإنترنت تعمل على إلغاء تجزئة الصفحات الورقية في صفحة فهرس مجمعة أو غير مجمعة تلو الأخرى باستخدام مساحة عمل إضافية صغيرة.REBUILD
هي عملية عبر الإنترنت في إصدارات Enterprise، وغير متصلة بالإنترنت في الإصدارات الأخرى، وتستخدم مساحة عمل إضافية بقدر حجم الفهرس مرة أخرى.يقوم بإنشاء نسخة جديدة من الفهرس ثم يسقط النسخة القديمة، وبالتالي التخلص من التجزئة.تتم إعادة حساب الإحصائيات بشكل افتراضي كجزء من هذه العملية، ولكن يمكن تعطيل ذلك.
يرى إعادة تنظيم وإعادة بناء الفهارس للمزيد من المعلومات.
لا تستخدم SHRINK
إلا مع TRUNCATEONLY
الخيار، وحتى إذا كان الملف سينمو مرة أخرى، فيجب عليك التفكير مليًا فيما إذا كان ذلك ضروريًا أم لا:
نصائح أخرى
إعادة التنظيم وإعادة البناء أمران مختلفان.
إعادة التنظيم:إنه إلغاء تجزئة للفهارس.يأخذ الفهرس (الفهارس) الموجودة ويقوم بإلغاء تجزئة الصفحات الموجودة.ومع ذلك، إذا لم تكن الصفحات متجاورة، فإنها تظل كما كانت من قبل.فقط محتوى الصفحات هو الذي يتغير.
إعادة بناء:في الواقع يقوم بإسقاط الفهرس وإعادة بنائه من الصفر.هذا يعني أنك ستحصل على فهرس جديد تمامًا، بصفحات مجزأة ومتجاورة.
علاوة على ذلك، من خلال إعادة البناء، يمكنك تغيير التقسيم أو مجموعات الملفات، ولكن مع إعادة التنظيم، لا يمكنك إلغاء تجزئة الفهرس بأكمله فحسب، بل أيضًا قسم واحد فقط من الفهرس.
تكون إحصائيات التحديث تلقائية على الفهارس المجمعة، ولكن ليس على الفهارس غير المجمعة.
قبل النظر في صيانة الفهارس، من المهم الإجابة على سؤالين رئيسيين:
- ما هي درجة التجزئة؟
- ما هو الإجراء المناسب؟إعادة التنظيم أم البناء؟
كما هو موضح في هذه المقالة 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