如何配置 Ola Hallengren IndexOptimize 腳本以針對錶的子集執行
我正在使用 Ola Hallengren IndexOptimize 腳本來處理大小為 7 TB、包含超過 300,000 個表的 SQL 2016 數據庫。我每晚只有 6 小時的時間來管理索引。我正在使用 timelimit 參數在 6 小時後停止工作。
問題是,每天晚上索引作業都從索引的開頭按字母順序開始,並且只能通過大約相同的 4,000 個表。
我該怎麼做才能讓索引作業覆蓋數據庫中的所有索引?也許通過創建多個作業,每週的每個晚上一個執行索引子集的作業?或者有沒有辦法讓工作在之前停止的第二天重新開始?
所有表都在同一個數據庫模式中。這是供應商提供的數據庫,我無法更改數據庫架構。
提前感謝您的任何指導。
我目前的工作步驟如下:
EXECUTE [dbo].[IndexOptimize] @Databases = 'USER_DATABASES', @FragmentationLow = NULL, @FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', @FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', @FragmentationLevel1 = 10, @FragmentationLevel2 = 40, @UpdateStatistics = 'ALL', @OnlyModifiedStatistics = 'Y', @PartitionLevel = 'N', @MaxDOP = 0, @SortInTempdb = 'Y', @TimeLimit = 21600, @LogToTable = 'Y'
不要每晚對索引進行碎片整理。您不再需要使用現代硬體(尤其是使用零尋軌時間的固態儲存)。對於日常維護,您只需要更新已修改的統計資訊,即使這樣,您也只需在發現性能下降時執行此操作(即檢查您的監控系統告訴您的資訊)。
根據 Ola 的網站,您可以執行以下操作:
EXECUTE dbo.IndexOptimize @Databases = 'USER_DATABASES', @FragmentationLow = NULL, @FragmentationMedium = NULL, @FragmentationHigh = NULL, @UpdateStatistics = 'ALL', @OnlyModifiedStatistics = 'Y'
當您確實需要對索引進行碎片整理時(例如,如果您刪除了大量數據),您可以做的另一件事是使用 SQL Server 的Service Broker為一批中的多個表設置維護計劃。
那篇文章深入且難以在此回復中進行總結,但歸根結底是創建一個非同步隊列,該隊列並行觸發代理作業。
philcart所做的應該可以幫助您“在之前停止的第二天重新開始” :
我在兩個 SQL 代理作業中執行 IndexOptimise。
在每週執行的第一個作業中,我使用以下內容提取所有需要優化的索引,
@FragmentationLow = NULL, @FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', @FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE', @FragmentationLevel1 = 5, @FragmentationLevel2 = 30, @LogToTable = 'Y', @Execute = 'N';
這將擷取 CommandLog 表中的索引重建/碎片整理命令。執行 IndexOptimise 命令後,所有剛剛插入的記錄的“EndTime”列都設置為空。
然後每天,在我們的維護視窗內,第二個作業只使用一個簡單的游標來拉出每個 EndTime 為 NULL 的命令,並在時間視窗尚未過去時執行它。
DECLARE @sqlCmd nvarchar(max); DECLARE @maxDuration int = 60; DECLARE @startTime datetime = GETDATE(); DECLARE @totalCmds int = (SELECT COUNT(1) FROM [dbo].[CommandLog] WHERE [EndTime] IS NULL); DECLARE @currCount int = 0; DECLARE @cmdSample nvarchar(100); IF @totalCmds > 0 BEGIN -- we have work to do DECLARE cmds CURSOR FAST_FORWARD FOR SELECT [ID],[Command] FROM [dbo].[CommandLog] WHERE [EndTime] IS NULL ORDER BY [ID] DESC OPEN cmds FETCH NEXT FROM cmds INTO @logID, @sqlCmd WHILE (@@FETCH_STATUS = 0 AND (DATEDIFF(MI,@startTime,GETDATE()) < @maxDuration)) BEGIN SET @currCount += 1; SET @cmdSample = LEFT(@sqlCmd,60)+'...'; UPDATE [dbo].[CommandLog] SET [StartTime] = GETDATE() WHERE [ID] = @logID; RAISERROR('Excuting IndexOptimize command for ID:%i (%i of %i) - %s',10,1,@logID, @currCount, @totalCmds,@cmdSample) WITH NOWAIT; EXEC sp_executeSql @command = @sqlCmd; UPDATE [dbo].[CommandLog] SET [EndTime] = GETDATE() WHERE [ID] = @logID; RAISERROR('Command complete for ID:%i (%i of %i) - %s',10,1,@logID, @currCount, @totalCmds,@cmdSample) WITH NOWAIT; FETCH NEXT FROM cmds INTO @logID, @sqlCmd END IF (@currCount < @totalCmds) BEGIN RAISERROR('IndexOptimize finishing due to elapsed time, executed %i commands out of %i',10,1,@currCount, @totalCmds) WITH NOWAIT; END ELSE BEGIN RAISERROR('All commands executed within allowed time window',10,1) WITH NOWAIT; END CLOSE cmds DEALLOCATE cmds END -- we have work to do ELSE BEGIN -- we have work to do RAISERROR('IndexOptimize has nothing to execute',10,1) WITH NOWAIT; END -- we have work to do