繁体   English   中英

SP 耗时 15 分钟,但执行时相同的查询在 1-2 分钟内返回结果

[英]SP taking 15 minutes, but the same query when executed returns results in 1-2 minutes

所以基本上我有这个相对较长的存储过程。 基本的执行流程是它将一些数据SELECTS INTO到用#符号声明的临时表中,然后在这些表中运行游标,生成一个“运行总计”到使用CREATE创建的第三个临时表中。 然后这个生成的临时表与数据库中的其他表连接以在一些分组等之后生成结果。问题是这个 SP 一直运行良好,直到现在在 1-2 分钟内返回结果。 现在突然需要 12-15 分钟。 如果我从 SP 中提取查询并通过手动设置相同的参数在管理工作室中执行它,它会在 1-2 分钟内返回结果,但 SP 需要很长时间。 知道会发生什么。 我试图生成查询和 SP 的实际执行计划,但由于游标而无法生成它。 知道为什么 SP 需要这么长时间而查询却没有吗?

这是参数嗅探的足迹。 有关它的另一个讨论,请参见此处; SQL糟糕的存储过程执行计划性能——参数嗅探

有几种可能的修复方法,包括将 WITH RECOMPILE 添加到您的存储过程中,这大约可以在一半的时间内工作。

对于大多数情况(尽管这取决于查询和 sproc 的结构),建议的修复方法是不要直接在查询中使用参数,而是将它们存储到局部变量中,然后在查询中使用这些变量。

这是由于参数嗅探。 首先声明临时变量并将传入的变量值设置为临时变量,并在整个应用程序中使用临时变量,下面是一个示例。

ALTER PROCEDURE [dbo].[Sp_GetAllCustomerRecords]
@customerId INT 
AS
declare @customerIdTemp INT
set @customerIdTemp = @customerId
BEGIN
SELECT * 
FROM   Customers e Where
CustomerId = @customerIdTemp 
End

试试这个方法

尝试重新编译 sproc 以丢弃任何存储的查询计划

exec sp_recompile 'YourSproc'

然后运行您的 sproc,注意使用合理的参数。

还要比较执行查询的两种方法之间的实际执行计划。

重新计算任何统计数据也可能是值得的。

我也会研究参数嗅探。 可能是 proc 需要以不同的方式处理参数。

我通常通过使用“print getdate() + ' - step '”来解决此类问题。 这有助于我缩小花费最多时间的范围。 您可以从查询分析器中运行它的位置进行比较,并缩小问题所在的范围。

我猜这可能归结为缓存。 如果您运行存储过程两次,第二次会更快吗?

要进一步调查,您可以从管理工作室的存储过程和查询版本中运行它们,并在管理工作室中打开显示查询计划选项,然后比较存储过程中哪个区域比作为查询运行时花费的时间更长。

或者,您可以在此处发布存储过程,供人们提出优化建议。

首先,基于使用多个临时表(可以保存在内存中,或持久化到 tempdb - 无论 SQL Server 决定什么是最好的),SQL 听起来都不会表现得很好,并且游标的使用。

我的建议是看看您是否可以将 sproc 重写为基于集合的查询而不是游标方法,这将提供更好的性能并且更容易调整和优化。 显然,我并不确切知道您的 sproc 是做什么的,以表明这对您来说有多容易/可行。

至于为什么 SP 需要比查询更长的时间 - 很难说。 当您尝试每种方法时,系统上的负载是否相同? 如果您在负载较轻时运行查询本身,这会比在负载较重时运行 SP 时更好。

此外,为了确保查询确实比 SP 更快,您需要排除数据/执行计划缓存,这使得后续运行的查询更快。 您可以使用以下方法清除缓存:

DBCC FREEPROCCACHE
DBCC DROPCLEANBUFFERS

但只能在开发/测试数据库服务器上执行此操作,而不是在生产上执行此操作。 然后运行查询,记录统计信息(例如来自分析器)。 再次清除缓存。 运行 SP 并比较统计数据。

1) 当您第一次运行查询时,可能需要更多时间。 还有一点是,如果您正在使用任何相关的子查询,并且如果您对值进行硬编码,它将只执行一次。 当您不对其进行硬编码并通过该过程运行它时,如果您尝试从输入值中导出值,则可能需要更多时间。

2) 在极少数情况下,这可能是由于网络流量造成的,对于相同的输入数据,我们在查询执行时间上也不会具有一致性。

我也遇到了一个问题,我们必须创建一些临时表,然后操作它们必须根据规则计算一些值,最后将计算出的值插入第三个表中。 这一切如果放入单个 SP 大约需要 20-25 分钟。 因此,为了进一步优化它,我们将 sp 分成 3 个不同的 sp,现在花费的总时间约为 6-8 分钟。 只需确定整个过程中涉及的步骤以及如何将它们分解为不同的 sp。 当然,通过使用这种方法,整个过程所花费的总时间会减少。

这是因为参数截断。 但是你怎么能确认呢?

每当我们应该优化 SP 时,我们都会寻找执行计划。 但在您的情况下,您将看到来自 SSMS 的优化计划,因为只有在通过代码调用时才会花费更多时间。

对于每个 SP 和函数,由于 ARITHABORT 选项,SQL 服务器生成两个估计计划。 一个用于 SSMS,第二个用于外部实体 (ADO Net)。

ARITHABORT 在 SSMS 中默认关闭。 因此,如果您想检查您的 SP 在从 Code 调用时使用的确切查询计划。

只需启用 SSMS 中的选项并执行您的 SP,您将看到 SP 也将从 SSMS 中花费 12-13 分钟。 SET ARITHABORT ON EXEC YourSpName SET ARITHABORT OFF

要解决这个问题,您只需要更新估计查询计划。

有几种方法可以更新估计查询计划。 1. 更新表统计信息。 2. 重新编译 SP 3. 在 SP 中设置 ARITHABORT OFF,因此它将始终使用为 SSMS 创建的查询计划(不推荐此选项)有关更多选项,请参阅这篇很棒的文章 - http://www.sommarskog.se/query-计划-mysteries.html

我建议该问题与临时表的类型(# 前缀)有关。 此临时表保存该数据库会话的数据。 当您通过应用程序运行它时,临时表将被删除并重新创建。
您可能会发现在 SSMS 中运行时,它会保留会话数据并更新表而不是创建它。 希望有帮助:)

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM