IndexOptimize之后查询和更新非常慢


12

数据库SQL Server 2017 Enterprise CU16 14.0.3076.1

最近,我们尝试从默认的“索引重建”维护作业切换到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'

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

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

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

update语句执行速度慢时的执行计划。即
在IndexOptimize
实际执行计划之后(尽快推出)

我还没发现差异。
快速计划相同的查询
实际执行计划

Answers:


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

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


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

<StatisticsInfo LastUpdate="2019-09-01T01:07:46.04" ModificationCount="0" 
    SamplingPercent="2.18233" Statistics="[INDX_UPP_4]" Table="[UPPDRAG]" 
    Schema="[SVALA]" Database="[ulek-sva]" />

与快速查询(100%):

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

@JoshDarnell大声笑,这是第二次出现,您在我未能看到的查询计划中找到了一些支持统计信息。感谢您的编辑!
John Eisbrener

哈哈,我忘记了你,约翰!我保证我不会跟踪你
乔什·达内尔

@JoshDarnell我感谢其他见解,这再次提醒您,执行计划中有太多信息,您不应该跳过这些信息。
John Eisbrener

乐意效劳!是的,我也一直想念某些东西(我被统计数据所困扰,所以我倾向于快速去那里看看发生了什么)。
乔什·达内尔

谢谢您的解释,确实是问题所在。大多数统计信息的默认抽样率为2.2%,但是从Oracle迁移后创建的一些统计数据的抽样率为100%。似乎默认的索引重建保持了100%,但是当我们使用IndexOptimize时,它也将默认值重建为2.2%。应用@StatisticsSample参数并再次运行查询,验证这是引起问题的原因。
MartinBergström19年

5

John的答案是正确的解决方案,这仅仅是对执行计划的哪些部分进行了更改,并提供了如何使用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)
                      )

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

当性能降低时,此更新查询的估计查询计划具有很高的估计:

在此处输入图片说明

尽管实际上(实际的执行计划)它仍然需要工作,但并不是估算所显示的疯狂数量。

对性能的最大影响是以下两个扫描和哈希匹配联接:

实际扫描性能下降#1

在此处输入图片说明

实际扫描性能下降#2

在此处输入图片说明


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

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

在此处输入图片说明

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

在此处输入图片说明

在此处输入图片说明

在此处输入图片说明

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


1
感谢您对计划的详细说明,这对我理解问题发生原因非常有帮助。我一定会看一下Sentry One Plan Explorer,它看起来非常有用!
MartinBergström19年

@MartinBergström真高兴,谢谢您提供查询计划并向我们提供我们在评论中询问的所有相关信息:)。关于计划浏览器的最好的事情是它是免费的!它也可以从ssms内部工作(通过右键单击执行计划,然后按“使用sendryone计划浏览器查看”)。
Randi Vertongen

1

没有更多信息,我们只能在黑暗中采取轻率的行动,因此您应该编辑问题以提供更多信息。例如,在索引维护操作之前和之后,您已为其指定了定时的更新语句的查询计划,因为该计划可能由于更新了索引统计信息而有所不同(https://www.brentozar.com/pastetheplan /对此很有用,而不是用可能是大量XML的内容填充问题或给出不包含计划文本所包含的某些相关信息的屏幕抓图)。

但是,有两个非常简单的要点:

  1. 优化运行是否已完成?如果您的测试与长期运行的索引重建的IO竞争,则会影响计时。
  2. 您测试了多次吗?如果更新基于考虑大量数据的查询中的数据(而不是简单的“ UPDATE TheTable SET ThisColumn ='A Static Value'”),则可能是该数据通常在内存中,但已被刷新出由于打磁盘而不是在内存的缓冲池中找到所需的页面,因此相关查询的第一次运行比平时慢。

感谢您抽出宝贵的时间回复。我已经通过粘贴计划链接更新了问题。优化肯定已经完成,它在出现问题的前一天运行了大约1个小时。我们做了多次测试,实际上它影响了以相同方式在两个不同测试环境中运行的数据库的两个副本。Update语句只是我找到的最简单的示例,还有许多其他插入内容和选择内容受到影响
MartinBergström19年

“多次”是指在索引的一个实例发生更改后尝试多次更新,而不是独立运行索引优化脚本多次(尽管这本身是验证结果可重现的一种有用方法)。如果内存刷新是问题(或问题的一部分),则第一个“从选择中进行更新”将启动缓冲池,因此,由于IO大大减少,后一个更新可能会更快。
David Spillett

如果我的回复不清楚,我们深表歉意。是的,我们多次尝试了更新。速度下降发生在测试人员用来测试应用程序以及查询和更新的数据库上,该数据库在一天中多次运行,而性能没有得到改善。
马丁·伯格斯特罗姆(MartinBergström),
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.