Joe*_*idt 4 index sql-server ola-hallengren index-maintenance sql-server-2017
我尝试在IndexOptimize索引数量非常多(> 250.000)的数据库上使用存储过程。存储过程收集需要处理的数据的初始步骤需要数小时,即使我设置了 @Indexes 参数来缩小工作范围。
SQL Server 维护解决方案版本:2019-02-10 10:40:47 安装了最新 CU14 的 SQL Server 2017 标准版。
在客户那里,我看到了一个包含 500.000 个以上索引的数据库。12 小时后,数据收集步骤仍在运行。
我希望如果我将 @Indexes 设置为单个索引执行应该立即开始。
这是我的存储过程调用的示例。
EXECUTE dbo.IndexOptimize
@Databases = 'db',
@Indexes = 'db.dbo.'
@FragmentationLow = NULL,
@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationLevel1 = 30,
@FragmentationLevel2 = 50,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y',
@LogToTable = 'Y',
@LockTimeout = 60
Run Code Online (Sandbox Code Playgroud)
有人可以与我分享他在索引数量非常多的数据库中使用 IndexOptimize 的经验吗?
有人可以与我分享他在索引数量非常多的数据库中使用 IndexOptimize 的经验吗?
你是对的,Ola 的脚本首先从所有与索引相关的 DMV 中获取数据并将它们插入到 中@tmpIndexesStatistics,即使指定了一个表或一个索引。对于很多索引,它会卡在第一个命令上,这是一个相当大的查询。请参阅下面的查询,甚至在下面进一步了解可能的解决方法。
TL; 博士
在其中一个查询中,动态管理视图被调用了几次,每个视图都有多个过滤器。
所有这些都导致了一个相当大的执行计划。
在运行查询之前创建临时表并存储来自 DMV 的数据是一种解决方法。您可以使用这些临时表/重写上的索引进一步优化。
测试数据:>100K 空表 +>200k 空索引
USE Test2
GO
SET NOCOUNT ON;
DECLARE @SQL NVARCHAR(MAX)
DECLARE @i int = 0
DECLARE @counter int = 1000
WHILE @i <+ @counter
BEGIN
SET @SQL = N'CREATE TABLE dbo.'+QUOTENAME(cast(@i as nvarchar(20)))+'( id int, val varchar(255));'
EXEC(@sql)
SET @SQL = N'CREATE INDEX IX_'+cast(@i as nvarchar(20))+ ' ON '+QUOTENAME(cast(@i as nvarchar(20)))+'(id) '
EXEC(@sql)
SET @SQL = N'CREATE INDEX IX_'+cast(@i as nvarchar(20))+ '_2 ON '+QUOTENAME(cast(@i as nvarchar(20)))+'(val) '
EXEC(@sql)
set @I +=1
IF @i = @counter
BEGIN
IF @counter < 100000
BEGIN
SET @counter += 1000;
END
END
END
select count(*) from sys.tables;
--100731
select count(*) from sys.indexes where index_id != 0;
--201614
Run Code Online (Sandbox Code Playgroud)
更改参数以处理所有索引时,并打印而不是执行语句 ( @Execute='N')
EXECUTE MNGDB.dbo.IndexOptimize
@Databases = 'test2',
@Indexes = 'test2.dbo.',
@FragmentationLow = NULL,
@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationLevel1 = 30,
@FragmentationLevel2 = 50,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y',
@LogToTable = 'Y',
@LockTimeout = 60,
@Execute='N';
Run Code Online (Sandbox Code Playgroud)
这个巨大的查询开始发挥作用
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
USE [test2];
SELECT SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, IsImageText, IsNewLOB, IsFileStream, IsColumnStore, IsComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, NoRecompute, IsIncremental, PartitionID, PartitionNumber, PartitionCount, [Order], Selected, Completed
FROM (SELECT schemas.[schema_id] AS SchemaID, schemas.[name] AS SchemaName, objects.[object_id] AS ObjectID, objects.[name] AS ObjectName, RTRIM(objects.[type]) AS ObjectType, tables.is_memory_optimized AS IsMemoryOptimized, indexes.index_id AS IndexID, indexes.[name] AS IndexName, indexes.[type] AS IndexType, indexes.allow_page_locks AS AllowPageLocks,
CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id WHERE columns.[object_id] = objects.object_id AND types.name IN('image','text','ntext')) THEN 1 ELSE 0 END AS IsImageText,
CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE columns.[object_id] = objects.object_id AND (types.name IN('xml') OR (types.name IN('varchar','nvarchar','varbinary') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1))) THEN 1 WHEN indexes.[type] = 2
AND EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.[object_id] = columns.[object_id]
AND index_columns.column_id = columns.column_id
INNER JOIN sys.types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND (types.[name] IN('xml') OR (types.[name] IN('varchar','nvarchar','varbinary') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1)))
THEN 1 ELSE 0 END AS IsNewLOB,
CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM sys.columns columns WHERE columns.[object_id] = objects.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END AS IsFileStream,
CASE WHEN EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] IN(5,6)) THEN 1 ELSE 0 END AS IsColumnStore,
CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.object_id = columns.object_id AND index_columns.column_id = columns.column_id WHERE (index_columns.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND columns.is_computed = 1 AND index_columns.object_id = indexes.object_id AND index_columns.index_id = indexes.index_id) THEN 1 ELSE 0 END AS IsComputed,
CASE WHEN EXISTS(SELECT * FROM sys.index_columns index_columns INNER JOIN sys.columns columns ON index_columns.[object_id] = columns.[object_id] AND index_columns.column_id = columns.column_id INNER JOIN sys.types types ON columns.system_type_id = types.system_type_id
WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND types.[name] = 'timestamp') THEN 1 ELSE 0 END AS IsTimestamp,
CASE WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.destination_data_spaces destination_data_spaces ON indexes.data_space_id = destination_data_spaces.partition_scheme_id INNER JOIN sys.filegroups filegroups ON destination_data_spaces.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes2.[object_id] = indexes.[object_id] AND indexes2.[index_id] = indexes.index_id AND destination_data_spaces.destination_id = partitions.partition_number) THEN 1 WHEN EXISTS (SELECT * FROM sys.indexes indexes2 INNER JOIN sys.filegroups filegroups ON indexes.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes.[object_id] = indexes2.[object_id] AND indexes.[index_id] = indexes2.index_id) THEN 1 WHEN indexes.[type] = 1 AND EXISTS (SELECT * FROM sys.tables tables INNER JOIN sys.filegroups filegroups ON tables.lob_data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND tables.[object_id] = objects.[object_id]) THEN 1 ELSE 0 END AS OnReadOnlyFileGroup, CASE WHEN EXISTS(SELECT * FROM sys.index_resumable_operations index_resumable_operations WHERE state_desc = 'PAUSED' AND index_resumable_operations.object_id = indexes.object_id AND index_resumable_operations.index_id = indexes.index_id AND (index_resumable_operations.partition_number = partitions.partition_number OR index_resumable_operations.partition_number IS NULL)) THEN 1 ELSE 0 END AS ResumableIndexOperation, stats.stats_id AS StatisticsID, stats.name AS StatisticsName, stats.no_recompute AS NoRecompute, stats.is_incremental AS IsIncremental, partitions.partition_id AS PartitionID, partitions.partition_number AS PartitionNumber, IndexPartitions.partition_count AS PartitionCount, 0 AS [Order], 0 AS Selected, 0 AS Completed FROM sys.indexes indexes INNER JOIN sys.objects objects ON indexes.[object_id] = objects.[object_id] INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id] LEFT OUTER JOIN sys.tables tables ON objects.[object_id] = tables.[object_id] LEFT OUTER JOIN sys.stats stats ON indexes.[object_id] = stats.[object_id] AND indexes.[index_id] = stats.[stats_id] LEFT OUTER JOIN sys.partitions partitions ON indexes.[object_id] = partitions.[object_id] AND indexes.index_id = partitions.index_id LEFT OUTER JOIN (SELECT partitions.[object_id], partitions.index_id, COUNT(DISTINCT partitions.partition_number) AS partition_count FROM sys.partitions partitions GROUP BY partitions.[object_id], partitions.index_id) IndexPartitions ON partitions.[object_id] = IndexPartitions.[object_id] AND partitions.[index_id] = IndexPartitions.[index_id] WHERE objects.[type] IN('U','V') AND objects.is_ms_shipped = 0 AND indexes.[type] IN(1,2,3,4,5,6,7) AND indexes.is_disabled = 0 AND indexes.is_hypothetical = 0
UNION
SELECT schemas.[schema_id] AS SchemaID, schemas.[name] AS SchemaName, objects.[object_id] AS ObjectID, objects.[name] AS ObjectName, RTRIM(objects.[type]) AS ObjectType, tables.is_memory_optimized AS IsMemoryOptimized, NULL AS IndexID, NULL AS IndexName, NULL AS IndexType, NULL AS AllowPageLocks, NULL AS IsImageText, NULL AS IsNewLOB, NULL AS IsFileStream, NULL AS IsColumnStore, NULL AS IsComputed, NULL AS IsTimestamp, NULL AS OnReadOnlyFileGroup, NULL AS ResumableIndexOperation, stats.stats_id AS StatisticsID, stats.name AS StatisticsName, stats.no_recompute AS NoRecompute, stats.is_incremental AS IsIncremental, NULL AS PartitionID, dm_db_incremental_stats_properties.partition_number AS PartitionNumber, NULL AS PartitionCount, 0 AS [Order], 0 AS Selected, 0 AS Completed FROM sys.stats stats INNER JOIN sys.objects objects ON stats.[object_id] = objects.[object_id] INNER JOIN sys.schemas schemas ON objects.[schema_id] = schemas.[schema_id] LEFT OUTER JOIN sys.tables tables ON objects.[object_id] = tables.[object_id] OUTER APPLY sys.dm_db_incremental_stats_properties(stats.object_id, stats.stats_id) dm_db_incremental_stats_properties WHERE objects.[type] IN('U','V') AND objects.is_ms_shipped = 0 AND NOT EXISTS(SELECT * FROM sys.indexes indexes WHERE indexes.[object_id] = stats.[object_id] AND indexes.index_id = stats.stats_id)) IndexesStatistics
Run Code Online (Sandbox Code Playgroud)
现在,当我们尝试在内部查找触发此命令的内容时 [dbo].[IndexOptimize]
IF EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IN('ALL','INDEX')
Run Code Online (Sandbox Code Playgroud)
与@ActionsPreferred对应的fragmentationlevel 动作和@UpdateStatistics被更新统计信息。这意味着它会在我们进行统计更新或索引重建/重组时运行。
没有应用过滤,查询会在数据库中的所有索引上运行。
因此,目前我们将不得不经历所有这些麻烦,即使我们指定了一张表。
EXECUTE MNGDB.dbo.IndexOptimize
@Databases = 'test2',
@Indexes = 'test2.dbo.[83631]',
@FragmentationLow = NULL,
@FragmentationMedium = 'INDEX_REORGANIZE,INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationHigh = 'INDEX_REBUILD_ONLINE,INDEX_REBUILD_OFFLINE',
@FragmentationLevel1 = 30,
@FragmentationLevel2 = 50,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y',
@LogToTable = 'Y',
@LockTimeout = 60,
@EXECUTE= 'N'
Run Code Online (Sandbox Code Playgroud)
实际的索引过滤发生在巨大的命令运行之后:
IF @Indexes IS NULL
BEGIN
UPDATE tmpIndexesStatistics
SET tmpIndexesStatistics.Selected = 1
FROM @tmpIndexesStatistics tmpIndexesStatistics
END
ELSE
BEGIN
...
Run Code Online (Sandbox Code Playgroud)
并相应地更新tmpIndexesStatistics表,以便对这些索引执行索引操作。
这个查询在我的小型开发服务器上运行了 60 多分钟,然后再杀死它。
什么可能是解决此问题的方法?
好吧,您必须更改 ola 的脚本,但是带有临时表的解决方案对我有用,并且在 1 分钟内执行。毫无疑问,它可以进一步优化:
SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;
USE [test2];
select *
INTO #Indexes
from sys.indexes;
select *
INTO #Index_columns
FROM sys.index_columns;
SELECT *
INTO #columns
FROM SYS.columns;
SELECT *
INTO #types
FROM
sys.types;
SELECT *
INTO #destination_data_spaces
FROM sys.destination_data_spaces;
SELECT *
INTO #filegroups
FROM sys.filegroups;
SELECT *
INTO #stats
FROM sys.stats;
SELECT *
INTO #objects
FROM sys.objects;
SELECT *
INTO #partitions
FROM sys.partitions;
SELECT *
INTO #tables
FROM sys.tables;
SELECT *
INTO #index_resumable_operations
FROM sys.index_resumable_operations
SELECT *
INTO #schemas
FROM sys.schemas
+ rerun the query with temp tables instead (char limit)
Run Code Online (Sandbox Code Playgroud)
在索引优化过程中改变这一点
对不起,奥拉,看这会很痛
当我编写 proc 脚本时,这是从第 1430 行到 1537
IF DATABASEPROPERTYEX(@CurrentDatabaseName,'Status') = 'ONLINE'
AND (@CurrentIsDatabaseAccessible = 1 OR @CurrentIsDatabaseAccessible IS NULL)
AND DATABASEPROPERTYEX(@CurrentDatabaseName,'Updateability') = 'READ_WRITE'
BEGIN
-- Select indexes in the current database
IF (EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IS NOT NULL) AND (GETDATE() < DATEADD(ss,@TimeLimit,@StartTime) OR @TimeLimit IS NULL)
BEGIN
SET @CurrentCommand01 = 'SET TRANSACTION ISOLATION LEVEL READ UNCOMMITTED;'
+ 'USE ' + QUOTENAME(@CurrentDatabaseName) + ';
select *
INTO #Indexes
from sys.indexes;
select *
INTO #Index_columns
FROM sys.index_columns;
SELECT *
INTO #columns
FROM SYS.columns;
SELECT *
INTO #types
FROM
sys.types;
SELECT *
INTO #destination_data_spaces
from sys.destination_data_spaces;
SELECT *
INTO #filegroups
FROM sys.filegroups;
SELECT * kill 64
INTO #stats
from sys.stats;
SELECT *
INTO #objects
from sys.objects;
SELECT *
INTO #partitions
from sys.partitions;
SELECT *
INTO #tables
FROM sys.tables;
SELECT *
INTO #index_resumable_operations
FROM sys.index_resumable_operations;
SELECT *
INTO #schemas
FROM sys.schemas; '
+ ' SELECT SchemaID, SchemaName, ObjectID, ObjectName, ObjectType, IsMemoryOptimized, IndexID, IndexName, IndexType, AllowPageLocks, IsImageText, IsNewLOB, IsFileStream, IsColumnStore, IsComputed, IsTimestamp, OnReadOnlyFileGroup, ResumableIndexOperation, StatisticsID, StatisticsName, NoRecompute, IsIncremental, PartitionID, PartitionNumber, PartitionCount, [Order], Selected, Completed'
+ ' FROM ('
IF EXISTS(SELECT * FROM @ActionsPreferred) OR @UpdateStatistics IN('ALL','INDEX')
BEGIN
SET @CurrentCommand01 = @CurrentCommand01 + 'SELECT schemas.[schema_id] AS SchemaID'
+ ', schemas.[name] AS SchemaName'
+ ', objects.[object_id] AS ObjectID'
+ ', objects.[name] AS ObjectName'
+ ', RTRIM(objects.[type]) AS ObjectType'
+ ', ' + CASE WHEN @Version >= 12 THEN 'tables.is_memory_optimized' ELSE '0' END + ' AS IsMemoryOptimized'
+ ', indexes.index_id AS IndexID'
+ ', indexes.[name] AS IndexName'
+ ', indexes.[type] AS IndexType'
+ ', indexes.allow_page_locks AS AllowPageLocks'
+ ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM #columns columns INNER JOIN #types types ON columns.system_type_id = types.user_type_id WHERE columns.[object_id] = objects.object_id AND types.name IN(''image'',''text'',''ntext'')) THEN 1 ELSE 0 END AS IsImageText'
+ ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM #columns columns INNER JOIN #types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE columns.[object_id] = objects.object_id AND (types.name IN(''xml'') OR (types.name IN(''varchar'',''nvarchar'',''varbinary'') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1))) THEN 1'
+ ' WHEN indexes.[type] = 2 AND EXISTS(SELECT * FROM #Index_columns index_columns INNER JOIN #columns columns ON index_columns.[object_id] = columns.[object_id] AND index_columns.column_id = columns.column_id INNER JOIN #types types ON columns.system_type_id = types.user_type_id OR (columns.user_type_id = types.user_type_id AND types.is_assembly_type = 1) WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND (types.[name] IN(''xml'') OR (types.[name] IN(''varchar'',''nvarchar'',''varbinary'') AND columns.max_length = -1) OR (types.is_assembly_type = 1 AND columns.max_length = -1))) THEN 1 ELSE 0 END AS IsNewLOB'
+ ', CASE WHEN indexes.[type] = 1 AND EXISTS(SELECT * FROM #columns columns WHERE columns.[object_id] = objects.object_id AND columns.is_filestream = 1) THEN 1 ELSE 0 END AS IsFileStream'
+ ', CASE WHEN EXISTS(SELECT * FROM #Indexes indexes WHERE indexes.[object_id] = objects.object_id AND [type] IN(5,6)) THEN 1 ELSE 0 END AS IsColumnStore'
+ ', CASE WHEN EXISTS(SELECT * FROM #Index_columns index_columns INNER JOIN #columns columns ON index_columns.object_id = columns.object_id AND index_columns.column_id = columns.column_id WHERE (index_columns.key_ordinal > 0 OR index_columns.partition_ordinal > 0) AND columns.is_computed = 1 AND index_columns.object_id = indexes.object_id AND index_columns.index_id = indexes.index_id) THEN 1 ELSE 0 END AS IsComputed'
+ ', CASE WHEN EXISTS(SELECT * FROM #Index_columns index_columns INNER JOIN #columns columns ON index_columns.[object_id] = columns.[object_id] AND index_columns.column_id = columns.column_id INNER JOIN #types types ON columns.system_type_id = types.system_type_id WHERE index_columns.[object_id] = objects.object_id AND index_columns.index_id = indexes.index_id AND types.[name] = ''timestamp'') THEN 1 ELSE 0 END AS IsTimestamp'
+ ', CASE WHEN EXISTS (SELECT * FROM #Indexes indexes2 INNER JOIN #destination_data_spaces destination_data_spaces ON indexes.data_space_id = destination_data_spaces.partition_scheme_id INNER JOIN #filegroups filegroups ON destination_data_spaces.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes2.[object_id] = indexes.[object_id] AND indexes2.[index_id] = indexes.index_id' + CASE WHEN @PartitionLevel = 'Y' THEN ' AND destination_data_spaces.destination_id = partitions.partition_number' ELSE '' END + ') THEN 1'
+ ' WHEN EXISTS (SELECT * FROM #Indexes indexes2 INNER JOIN #filegroups filegroups ON indexes.data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND indexes.[object_id] = indexes2.[object_id] AND indexes.[index_id] = indexes2.index_id) THEN 1'
+ ' WHEN indexes.[type] = 1 AND EXISTS (SELECT * FROM #tables tables INNER JOIN #filegroups filegroups ON tables.lob_data_space_id = filegroups.data_space_id WHERE filegroups.is_read_only = 1 AND tables.[object_id] = objects.[object_id]) THEN 1 ELSE 0 END AS OnRea
| 归档时间: |
|
| 查看次数: |
492 次 |
| 最近记录: |