当以前快速的SQL查询开始运行缓慢时,如何查找问题的根源?


36

背景

我有一个针对SQL Server 2008 R2的查询,该查询联接和/或左联接约12个不同的“表”。该数据库相当大,有许多表超过5000万行和大约300个不同的表。适用于在全国设有10个仓库的大型公司。所有仓库都读取和写入数据库。所以它很大而且很忙。

我遇到的查询看起来像这样:

select t1.something, t2.something, etc.
from Table1 t1
    inner join Table2 t2 on t1.id = t2.t1id
    left outer join (select * from table 3) t3 on t3.t1id = t1.t1id
    [etc]...
where t1.something = 123

请注意,联接之一是在不相关的子查询上。

问题是从今天早上开始,在没有对系统进行任何更改(我或我的团队中的任何人都知道)的情况下,该查询通常需要运行2分钟左右,而开始需要花一个半小时才能运行-跑了。数据库的其余部分运行正常。我已经从通常运行该程序的过程中取出了此查询,并以相同的慢度在带有硬编码参数变量的SSMS中运行了该查询。

奇怪的是,当我使用不相关的子查询并将其放入临时表中,然后使用该子查询代替子查询时,查询运行良好。另外(这对我来说是最奇怪的),如果我将这段代码添加到查询的末尾,则查询运行良好:

and t.name like '%'

我从这些小实验得出的结论(可能是错误的)是,放慢速度的原因是由于如何设置SQL的缓存执行计划-当查询略有不同时,它必须创建一个新的执行计划。

我的问题是这样的:当以前运行快速的查询突然在深夜开始运行,并且除此查询外没有其他影响,我该如何解决它以及如何避免将来发生?我怎么知道SQL在内部做的事情使其变得如此缓慢(如果运行了错误的查询,我可以得到它的执行计划,但它不会运行-也许预期的执行计划会给我一些东西?)?如果此问题与执行计划有关,那么如何避免SQL认为真正糟糕的执行计划是个好主意?

同样,这也不是参数嗅探的问题。我以前见过这种情况,不是这样,因为即使我在SSMS中对变量进行硬编码,性能仍然很慢。


您能否在此处共享查询计划(慢速查询): brentozar.com/pastetheplan
MJH,

Answers:


31

当曾经快速运行的查询突然在深夜开始缓慢运行并且除此查询外没有其他影响,我该如何解决...?

您可以先检查执行计划是否仍在缓存中。检查sys.dm_exec_query_statssys.dm_exec_procedure_statssys.dm_exec_cached_plans。如果错误的执行计划仍被缓存,则您可以对其进行分析,还可以检查执行状态。执行统计信息将包含逻辑读取,CPU时间和执行时间等信息。这些可以强有力地表明问题出在哪里(例如,大扫描与阻塞)。有关如何解释数据的说明,请参见识别问题查询

同样,这也不是参数嗅探的问题。我以前见过这种情况,不是这样,因为即使我在SSMS中对变量进行硬编码,性能仍然很慢。

我不相信。SSMS中的硬编码变量不能证明过去的错误执行计划未针对偏斜的输入进行编译。请阅读参数嗅探,嵌入和RECOMPILE选项,以获得有关该主题的非常好的文章。应用速度慢,SSMS快速吗?了解性能奥秘 是另一个很好的参考。

我从这些小实验得出的结论(可能是错误的)是,放慢速度的原因是由于如何设置SQL的缓存执行计划-当查询略有不同时,它必须创建一个新的执行计划。

这很容易测试。SET STATISTICS TIME ON将显示编译时间与执行时间。SQL Server:Statistics性能计数器还将显示编译是否是一个问题(坦率地说,我认为这不太可能)。

但是,您可能会遇到类似的问题:查询授权门。有关详细信息,请阅读了解SQL服务器内存授予。如果您的查询在没有可用内存的时刻请求大笔授权,它将不得不等待,并且对于应用程序而言,这一切都将视为“执行缓慢”。分析等待信息统计信息将显示是否是这种情况。

有关度量和查找内容的更一般性讨论,请参见如何分析SQL Server性能。


7

这是在SQL Server中运行复杂查询的祸根。幸运的是,这种情况很少发生。

查看查询的查询计划(查询运行缓慢时)。我猜想您会发现嵌套循环联接在没有联接索引的表上发生一次或多次。这确实减慢了速度。为了快进,解决此问题的方法是带有提示。在查询末尾添加以下内容:

OPTION (MERGE JOIN, HASH JOIN)

过去,这通常为我解决了这个问题。

可能发生的情况是,对表的细微更改(或对临时空间的可用性)导致SQL优化偏向于使用较慢的联接算法。这可能是非常微妙和突然的。创建临时表时,优化器会提供有关表的更多信息(例如其大小),因此它可以生成更好的计划。


1
实际上,执行计划是使用嵌套循环联接。但是,当我按照您的建议放置提示时,出现以下错误:“查询处理器由于此查询中定义的提示而无法生成查询计划。重新提交查询时不指定任何提示,也无需使用SET FORCEPLAN。” 当然,当我添加OPTION(LOOP JOIN)时,它将创建一个执行计划,但运行缓慢仍然存在问题。你遇到这个问题了吗?看起来它需要循环连接。

@特雷弗 。。不,我实际上还没有看到这个问题。也许您的查询正在执行一些非等联接(不使用等号),并且优化器必须在此使用嵌套循环联接。
Gordon Linoff 2014年

3

通常,它是导致这种问题的缺少索引。

我通常要做的是使用SQL Management Studio运行查询并启用“包括实际执行计划(CTRL + M)”,并找出哪个连接具有最大的百分比。

该应用程序并不关注瓶颈,但是您只需查看结果即可“快速”找到它。

这里的例子: 48%的顶部


2
索引不会突然“丢失”,因此尽管这可能会提高性能,但并不能解释问题
Fowl

3

我最近遇到了同样的问题,这使我进入了此页面。

当@MartinSmith建议更新您的统计信息并解释计划时,他遇到了一些麻烦。我想补充一点,您还应尝试确保您正在运行的作业/查询可能会导致锁定,从而减慢响应时间。

就我而言,罪魁祸首是工作收集表的统计数据。由于某种原因,它没有在应有的窗口中完成,并在用户恢复后继续运行。我找到了进程,将其终止,查询又开始响应。

我希望这可以帮助其他人


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.