Sql-Server

如何配置 Ola Hallengren IndexOptimize 腳本以針對錶的子集執行

  • May 21, 2020

我正在使用 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 

引用自:https://dba.stackexchange.com/questions/267095