当以前快速的 SQL 查询开始运行缓慢时,我应该在哪里查找问题的根源?

Tre*_*vor 39 performance sql-server-2008 sql-server

背景

我有一个针对 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
Run Code Online (Sandbox Code Playgroud)

请注意,其中一个连接位于不相关的子查询上。

问题是从今天早上开始,没有对系统进行任何更改(我或我团队中的任何人都知道),通常需要大约 2 分钟才能运行的查询,开始需要一个半小时才能运行——当它跑了。数据库的其余部分运行良好。我已经从它通常运行的 sproc 中取出了这个查询,并且我已经在 SSMS 中以相同的速度在带有硬编码参数变量的情况下运行它。

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

and t.name like '%'
Run Code Online (Sandbox Code Playgroud)

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

我的问题是:当一个曾经运行得很快的查询在半夜突然开始运行缓慢并且除了这个查询之外没有其他任何影响时,我该如何解决它以及如何防止它在未来发生? 我怎么知道 SQL 在内部做了什么使其如此缓慢(如果错误的查询运行,我可以获得它的执行计划但它不会运行——也许预期的执行计划会给我一些东西?)?如果这个问题与执行计划有关,我如何让 SQL 不认为真正糟糕的执行计划是个好主意?

此外,这不是参数嗅探的问题。我以前见过,但事实并非如此,因为即使我在 SSMS 中对变量进行硬编码,我的性能仍然很慢。

Rem*_*anu 33

当一个曾经运行速度很快的查询在半夜突然开始运行缓慢并且除了这个查询之外没有其他任何影响时,我该如何解决它......?

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

此外,这不是参数嗅探的问题。我以前见过,但事实并非如此,因为即使我在 SSMS 中对变量进行硬编码,我的性能仍然很慢。

我不相信。SSMS 中的硬编码变量并不能证明过去的错误执行计划不是针对倾斜的输入编译的。请阅读Parameter Sniffing, Embedding, and the RECOMPILE Options以获得关于该主题的一篇非常好的文章。应用程序慢,SSMS 快?理解性能之谜 是另一个很好的参考。

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

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

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

有关测量什么和查找什么的更一般性讨论,请参阅如何分析 SQL Server 性能


Gor*_*off 7

这是在 SQL Server 中运行复杂查询的祸根。幸运的是,这种情况并不经常发生。

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

OPTION (MERGE JOIN, HASH JOIN)
Run Code Online (Sandbox Code Playgroud)

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

可能发生的情况是对表(或临时空间的可用性)的细微更改导致 SQL 优化更喜欢较慢的连接算法。这可能非常微妙且非常突然。当您创建临时表时,优化器具有有关该表的更多信息(例如其大小),因此可以生成更好的计划。