You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

升级SMO至171版本后,MS SQL Server大型数据库脚本生成变慢

问题

我们运维的数据库包含大量架构与表,使用SMO(Microsoft.SqlServer.SqlManagementObjects)为单个架构生成对象创建脚本。例如某数据库有1200个架构,每个架构包含75个对象:

  • 使用SMO 150.18208.0版本时,本地SQL Server生成单个架构脚本耗时约12秒;
  • 升级至最新171.30.0版本后,耗时增至2-3分钟。

以下是创建Scripter的代码:

var scripter = new Scripter(sourceDb.SmoServer)
{
    Options =
    {
        DriAll = true,
        Indexes = true,
        ClusteredIndexes = true,
        NonClusteredIndexes = true,
        DriIndexes = true,
        DriClustered = true,
        DriNonClustered = true,
        FullTextIndexes = false,
        Triggers = true,
        Default = true,
        ScriptDataCompression = false,
        AllowSystemObjects = false,
        AgentJobId = false,
        ScriptXmlCompression = false
    }
};

生成脚本的代码(allSchemaObjects包含单个架构的全部75个对象):

scripts.AddRange(scripter.EnumScript(allSchemaObjects));

跟踪发现,每个对象都会执行一条耗时约1.7秒的查询语句:

exec sp_executesql N'SELECT
i.name AS [Name],
CAST(ISNULL(si.bounding_box_xmax,0) AS float(53)) AS [BoundingBoxXMax],
CAST(ISNULL(si.bounding_box_xmin,0) AS float(53)) AS [BoundingBoxXMin],
CAST(ISNULL(si.bounding_box_ymax,0) AS float(53)) AS [BoundingBoxYMax],
CAST(ISNULL(si.bounding_box_ymin,0) AS float(53)) AS [BoundingBoxYMin],
CAST(case when (i.type=7) then hi.bucket_count else 0 end AS int) AS [BucketCount],
CAST(ISNULL(si.cells_per_object,0) AS int) AS [CellsPerObject],
CAST(i.compression_delay AS int) AS [CompressionDelay],
~i.allow_page_locks AS [DisallowPageLocks],
~i.allow_row_locks AS [DisallowRowLocks],

        CASE WHEN ((SELECT tbli.is_memory_optimized FROM sys.tables tbli WHERE tbli.object_id = i.object_id)=1 or
        (SELECT tti.is_memory_optimized FROM sys.table_types tti WHERE tti.type_table_object_id = i.object_id)=1)
        THEN ISNULL((SELECT ds.name FROM sys.data_spaces AS ds WHERE ds.type=''FX''), N'''')
        ELSE CASE WHEN ''FG''=dsi.type THEN dsi.name ELSE N'''' END
        END
       AS [FileGroup],
CASE WHEN ''FD''=dstbl.type THEN dstbl.name ELSE N'''' END AS [FileStreamFileGroup],
CASE WHEN ''PS''=dstbl.type THEN dstbl.name ELSE N'''' END AS [FileStreamPartitionScheme],
i.fill_factor AS [FillFactor],
ISNULL(i.filter_definition, N'''') AS [FilterDefinition],
i.ignore_dup_key AS [IgnoreDuplicateKeys],

        ISNULL(indexedpaths.name, N'''')
       AS [IndexedXmlPathName],
i.is_primary_key + 2*i.is_unique_constraint AS [IndexKeyType],
CAST(
          CASE i.type WHEN 1 THEN 0 WHEN 4 THEN 4
                      WHEN 3 THEN CASE xi.xml_index_type WHEN 0 THEN 2 WHEN 1 THEN 3 WHEN 2 THEN 7 WHEN 3 THEN 8 END
                      WHEN 4 THEN 4 WHEN 6 THEN 5 WHEN 7 THEN 6 WHEN 5 THEN 9 ELSE 1 END
        AS tinyint) AS [IndexType],
CAST(CASE i.index_id WHEN 1 THEN 1 ELSE 0 END AS bit) AS [IsClustered],
i.is_disabled AS [IsDisabled],
CAST(CASE WHEN filetableobj.object_id IS NULL THEN 0 ELSE 1 END AS bit) AS [IsFileTableDefined],
CAST(ISNULL(k.is_system_named, 0) AS bit) AS [IsSystemNamed],
CAST(OBJECTPROPERTY(i.object_id,N''IsMSShipped'') AS bit) AS [IsSystemObject],
i.is_unique AS [IsUnique],
CAST(ISNULL(si.level_1_grid,0) AS smallint) AS [Level1Grid],
CAST(ISNULL(si.level_2_grid,0) AS smallint) AS [Level2Grid],
CAST(ISNULL(si.level_3_grid,0) AS smallint) AS [Level3Grid],
CAST(ISNULL(si.level_4_grid,0) AS smallint) AS [Level4Grid],
ISNULL(s.no_recompute,0) AS [NoAutomaticRecomputation],
CAST(ISNULL(INDEXPROPERTY(i.object_id, i.name, N''IsPadIndex''), 0) AS bit) AS [PadIndex],
ISNULL(xi2.name, N'''') AS [ParentXmlIndex],
CASE WHEN ''PS''=dsi.type THEN dsi.name ELSE N'''' END AS [PartitionScheme],
case UPPER(ISNULL(xi.secondary_type,'''')) when ''P'' then 1 when ''V'' then 2 when ''R'' then 3 else 0 end AS [SecondaryXmlIndexType],
CAST(ISNULL(spi.spatial_index_type,0) AS tinyint) AS [SpatialIndexType],
CAST(ISNULL(INDEXPROPERTY(i.object_id, i.name, N''IsOptimizedForSequentialKey''), 0) AS bit) AS [IsOptimizedForSequentialKey],
CAST(
        case when ((SELECT  MAX(case when xml_compression = 1 then 1 else 0 end) FROM sys.partitions  WHERE object_id = (CASE WHEN i.type = 4 THEN allobj.object_id ELSE i.object_id END) AND index_id = (CASE WHEN i.type = 4 THEN 1 ELSE i.index_id END)) > 0) then 1 else 0 end
       AS bit) AS [HasXmlCompressedPartitions]
FROM
sys.tables AS tbl
INNER JOIN sys.indexes AS i ON (i.index_id > @_msparam_0 and i.is_hypothetical = @_msparam_1) AND (i.object_id=tbl.object_id)
LEFT OUTER JOIN sys.spatial_index_tessellations as si ON i.object_id = si.object_id and i.index_id = si.index_id
LEFT OUTER JOIN sys.hash_indexes AS hi ON i.object_id = hi.object_id AND i.index_id = hi.index_id
LEFT OUTER JOIN sys.data_spaces AS dsi ON dsi.data_space_id = i.data_space_id
LEFT OUTER JOIN sys.tables AS t ON t.object_id = i.object_id
LEFT OUTER JOIN sys.data_spaces AS dstbl ON dstbl.data_space_id = t.Filestream_data_space_id and (i.index_id < 2 or (i.type = 7 and i.index_id < 3))
LEFT OUTER JOIN sys.xml_indexes AS xi ON xi.object_id = i.object_id AND xi.index_id = i.index_id
LEFT OUTER JOIN sys.selective_xml_index_paths AS indexedpaths ON xi.object_id = indexedpaths.object_id AND xi.using_xml_index_id = indexedpaths.index_id AND xi.path_id = indexedpaths.path_id
LEFT OUTER JOIN sys.filetable_system_defined_objects AS filetableobj ON i.object_id = filetableobj.object_id
LEFT OUTER JOIN sys.key_constraints AS k ON k.parent_object_id = i.object_id AND k.unique_index_id = i.index_id
LEFT OUTER JOIN sys.stats AS s ON s.stats_id = i.index_id AND s.object_id = i.object_id
LEFT OUTER JOIN sys.xml_indexes AS xi2 ON xi2.object_id = xi.object_id AND xi2.index_id = xi.using_xml_index_id
LEFT OUTER JOIN sys.spatial_indexes AS spi ON i.object_id = spi.object_id and i.index_id = spi.index_id
LEFT OUTER JOIN sys.all_objects AS allobj ON 
        allobj.name = ''extended_index_'' + cast(i.object_id AS varchar) + ''_'' + cast(i.index_id AS varchar) AND allobj.type=''IT''
      
WHERE
(tbl.name=@_msparam_2 and SCHEMA_NAME(tbl.schema_id)=@_msparam_3)
ORDER BY
[Name] ASC',N'@_msparam_0 nvarchar(4000),@_msparam_1 nvarchar(4000),@_msparam_2 nvarchar(4000),@_msparam_3 nvarchar(4000)',@_msparam_0=N'0',@_msparam_1=N'0',@_msparam_2=N'DomainItem_temp',@_msparam_3=N's19'

请问如何在使用最新SMO版本的前提下,恢复接近旧版本的性能?

解决方案

1. 精简冗余的脚本选项

当前启用的选项存在大量冗余:Indexes = true已经包含ClusteredIndexes和NonClusteredIndexes,DriAll也覆盖了DriIndexes、DriClustered、DriNonClustered。冗余选项会触发SMO执行额外的元数据查询,精简后可减少不必要的系统表访问:

var scripter = new Scripter(sourceDb.SmoServer)
{
    Options =
    {
        DriAll = true,
        Indexes = true,
        Triggers = true,
        Default = true,
        ScriptDataCompression = false,
        AllowSystemObjects = false,
        AgentJobId = false,
        ScriptXmlCompression = false
        // 移除所有重复的索引相关选项
    }
};

2. 预加载元数据后批量生成脚本

新版本SMO可能对逐个对象的元数据查询优化不足,可先预加载架构下所有对象的完整元数据,再一次性生成脚本:

// 预加载表和索引的默认字段,减少多次查询
sourceDb.SmoServer.SetDefaultInitFields(typeof(Table), true);
sourceDb.SmoServer.SetDefaultInitFields(typeof(Index), true);

// 确保所有对象已加载完整元数据
foreach (var obj in allSchemaObjects)
{
    obj.Load();
}

// 一次性生成整个架构的脚本
scripts.AddRange(scripter.EnumScript(allSchemaObjects));

3. 针对慢查询创建系统表索引

跟踪到的慢查询涉及多个系统表的关联,可创建以下索引加速查询(需在master数据库执行,且需高权限):

-- 优化sys.tables按schema和名称的查询
CREATE NONCLUSTERED INDEX IX_tables_schema_name ON sys.tables(schema_id, name) INCLUDE(object_id);

-- 优化sys.indexes按object_id的查询
CREATE NONCLUSTERED INDEX IX_indexes_object_id ON sys.indexes(object_id) INCLUDE(index_id, type, name, is_hypothetical, fill_factor);

4. 禁用不需要的特性脚本

新版本SMO默认收集空间索引、XML索引、内存优化表等特性的元数据,若你的业务不需要这些脚本,可显式禁用:

var scripter = new Scripter(sourceDb.SmoServer)
{
    Options =
    {
        // 保留必要选项
        DriAll = true,
        Indexes = true,
        Triggers = true,
        Default = true,
        ScriptDataCompression = false,
        AllowSystemObjects = false,
        AgentJobId = false,
        ScriptXmlCompression = false,
        // 禁用不需要的特性
        SpatialIndexes = false,
        XmlIndexes = false,
        MemoryOptimizedObjects = false,
        FileTables = false
    }
};

内容的提问来源于Stack Exchange,提问作者Sturle Dahl

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.18 13:29:50