IndexOptimize 后查询和更新速度极慢

Mar*_*röm 12 sql-server ola-hallengren index-maintenance sql-server-2017

数据库 SQL Server 2017 Enterprise CU16 14.0.3076.1

我们最近尝试从默认的 Index Rebuild 维护作业切换到 Ola Hallengren IndexOptimize。默认的索引重建作业已经运行了几个月没有任何问题,并且查询和更新的执行时间在可接受的范围内。在IndexOptimize数据库上运行后:

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 = 5,
@FragmentationLevel2 = 30,
@UpdateStatistics = 'ALL',
@OnlyModifiedStatistics = 'Y'
Run Code Online (Sandbox Code Playgroud)

性能极度下降。之前花费 100 毫秒的更新语句在之后IndexOptimize花费了 78.000毫秒(使用相同的计划),并且查询的性能也差了几个数量级。

由于这仍然是一个测试数据库(我们正在从 Oracle 迁移生产系统),我们恢复到备份并禁用IndexOptimize,一切恢复正常。

但是,我们想了解什么IndexOptimizeIndex Rebuild可能导致这种极端性能下降的“正常”不同,以确保我们在投入生产后避免这种情况。任何关于寻找什么的建议将不胜感激。

更新语句缓慢时的执行计划。即
IndexOptimize 之后的
实际执行计划(即将推出)

我一直无法发现差异。
快速时为同一个查询做
计划 实际执行计划

Joh*_*ner 11

我怀疑您在两种维护方法之间定义了不同的采样率。我相信 Ola 的脚本使用默认采样,除非您指定@StatisticsSample参数,这看起来不像您目前正在做的那样。

此时,这是推测,但您可以通过在数据库中运行以下查询来查看当前对统计数据使用的采样率:

SELECT  OBJECT_SCHEMA_NAME(st.object_id) + '.' + OBJECT_NAME(st.object_id) AS TableName
    ,   col.name AS ColumnName
    ,   st.name AS StatsName
    ,   sp.last_updated
    ,   sp.rows_sampled
    ,   sp.rows
    ,   (1.0*sp.rows_sampled)/(1.0*sp.rows) AS sample_pct
FROM sys.stats st 
    INNER JOIN sys.stats_columns st_col
        ON st.object_id = st_col.object_id
        AND st.stats_id = st_col.stats_id
    INNER JOIN sys.columns col
        ON st_col.object_id = col.object_id
        AND st_col.column_id = col.column_id
    CROSS APPLY sys.dm_db_stats_properties (st.object_id, st.stats_id) sp
ORDER BY 1, 2
Run Code Online (Sandbox Code Playgroud)

如果您看到这是 1s(例如 100%),那么这可能是您的问题。也许再次尝试 Ola 的脚本,包括@StatisticsSample带有此查询返回的百分比的参数,看看是否能解决您的问题?


作为该理论的额外支持证据,执行计划 XML 显示慢查询的采样率有很大不同(2.18233 %):

SELECT  OBJECT_SCHEMA_NAME(st.object_id) + '.' + OBJECT_NAME(st.object_id) AS TableName
    ,   col.name AS ColumnName
    ,   st.name AS StatsName
    ,   sp.last_updated
    ,   sp.rows_sampled
    ,   sp.rows
    ,   (1.0*sp.rows_sampled)/(1.0*sp.rows) AS sample_pct
FROM sys.stats st 
    INNER JOIN sys.stats_columns st_col
        ON st.object_id = st_col.object_id
        AND st.stats_id = st_col.stats_id
    INNER JOIN sys.columns col
        ON st_col.object_id = col.object_id
        AND st_col.column_id = col.column_id
    CROSS APPLY sys.dm_db_stats_properties (st.object_id, st.stats_id) sp
ORDER BY 1, 2
Run Code Online (Sandbox Code Playgroud)

与快速查询 (100%) 对比:

<StatisticsInfo LastUpdate="2019-08-25T23:01:05.52" ModificationCount="555" 
    SamplingPercent="100" Statistics="[INDX_UPP_4]" Table="[UPPDRAG]" 
    Schema="[SVALA]" Database="[ulek-sva]" />
Run Code Online (Sandbox Code Playgroud)


Ran*_*gen 5

约翰的答案是正确的解决方案,这只是关于执行计划的哪些部分发生变化的补充,以及如何轻松发现与Sentry One Plan explorer差异的示例

在 IndexOptimize 之前花费 100 毫秒的更新语句在之后花费了 78.000 毫秒(使用相同的计划)

查看性能下降时的所有查询计划时,您可以轻松发现差异。

性能下降

在此处输入图片说明

两次计数超过 35 秒的 CPU 时间和经过时间

预期表现

在此处输入图片说明

好多了

此更新查询的主要降级是两次:

UPDATE SVALA.INGÅENDEANALYS
                           SET 
                              UPPDRAGAVSLUTAT = @NEW$AVSLUTAT
                        WHERE INGÅENDEANALYS.ID IN 
                           (
                              SELECT IA.ID
                              FROM 
                                 SVALA.INGÅENDEANALYS  AS IA 
                                    JOIN SVALA.INGÅENDEANALYSX  AS IAX 
                                    ON IAX.INGÅENDEANALYS = IA.ID 
                                    JOIN SVALA.ANALYSMATERIAL  AS AM 
                                    ON AM.ID = IA.ANALYSMATERIALID 
                                    JOIN SVALA.ANALYSMATERIALX  AS AMX 
                                    ON AMX.ANALYSMATERIAL = AM.ID 
                                    JOIN SVALA.INSÄNTMATERIAL  AS IM 
                                    ON IM.ID = AM.INSÄNTMATERIALID 
                                    JOIN SVALA.INSÄNTMATERIALX  AS IMX 
                                    ON IMX.INSÄNTMATERIAL = IM.ID
                              WHERE IM.UPPDRAGSID = SVALA.PKGSVALA$STRIPVERSION(@NEW$ID)
                      )
Run Code Online (Sandbox Code Playgroud)

此查询的执行计划性能下降

当性能下降时,此更新查询的估计查询计划有非常高的估计:

在此处输入图片说明

虽然在现实中(实际的执行计划)它仍然需要工作,只是不是估计显示的疯狂数量。

对性能影响最大的是下面的两次扫描和哈希匹配连接:

对性能下降的实际扫描 #1

在此处输入图片说明

对性能下降的实际扫描 #2

在此处输入图片说明


具有预期性能的此查询的执行计划

当您将其与具有正常预期性能的查询计划的估计(或实际值)进行比较时,很容易发现差异。

在此处输入图片说明

此外,前两个表访问甚至没有发生:

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明

您不会在散列连接上看到这种消除,因为构建(顶部)输入首先插入到散列表中。然后在这个哈希表中探测零值,返回零值。