65

不久前,我有一个查询,我为我的一个用户运行了很多。它仍在不断发展和调整,但最终稳定下来并运行得非常快,因此我们从中创建了一个存储过程。

到目前为止,一切正常。

但是,存储过程很慢。查询和proc之间没有实质性区别,但速度变化很大。

[背景,我们正在运行 SQL Server 2005。]

一位友好的本地 DBA(不再在这里工作)看了一眼存储过程并说“参数欺骗!” (编辑:虽然它似乎也可能被称为“参数嗅探”,这可能解释了当我试图搜索它时谷歌点击量的缺乏。)

我们将一些存储过程抽象为第二个存储过程,将对这个新的内部过程的调用包装到预先存在的外部过程中,称为外部过程,嘿,转眼间,它与原始查询一样快。

那么,什么给了?有人可以解释参数欺骗吗?

奖励积分

  • 强调如何避免它
  • 建议如何识别可能的原因
  • 讨论缓解情况的替代策略,例如统计数据、索引、键
4

8 回答 8

55

仅供参考 - 当您使用 SQL 2005 并使用参数存储过程时,您需要注意其他事项。

SQL Server 将使用使用的第一个参数编译存储过程的执行计划。所以如果你运行这个:

usp_QueryMyDataByState 'Rhode Island'

执行计划最适用于小州的数据。但如果有人转身跑:

usp_QueryMyDataByState 'Texas'

为罗德岛大小的数据设计的执行计划可能不如德克萨斯大小的数据有效。当服务器重新启动时,这可能会产生令人惊讶的结果,因为新生成的执行计划将针对首先使用的任何参数——不一定是最好的。除非有很大的理由,否则不会重新编译该计划,例如重建统计数据。

这就是查询计划的用武之地,SQL Server 2008 提供了许多新功能,可帮助 DBA 长期将特定查询计划固定到位,无论首先调用什么参数。

我担心的是,当您重建存储过程时,您强制执行计划重新编译。你用你最喜欢的参数调用它,然后它当然很快 - 但问题可能不是存储过程。可能是存储过程在某些时候用一组不寻常的参数重新编译,因此查询计划效率低下。您可能没有修复任何东西,并且下次重新启动服务器或重新编译查询计划时,您可能会遇到同样的问题。

于 2008-10-19T01:36:33.493 回答
27

是的,我认为您的意思是参数嗅探,这是 SQL Server 优化器用来尝试找出参数值/范围的技术,以便它可以为您的查询选择最佳执行计划。在某些情况下,SQL Server 在参数嗅探方面做得很差,并且没有为查询选择最佳执行计划。

相信这篇博客文章 http://blogs.msdn.com/queryoptteam/archive/2006/03/31/565991.aspx有很好的解释。

您的示例中的 DBA 似乎选择了选项 #4 将查询移动到另一个存储过程到单独的过程上下文。

您还可以在原始存储过程上使用with recompile或在参数上使用优化选项。

于 2008-10-17T11:51:10.527 回答
27

一种加快速度的简单方法是在存储过程的最开始将输入参数重新分配给本地参数,例如

CREATE PROCEDURE uspParameterSniffingAvoidance
    @SniffedFormalParameter int
AS
BEGIN

    DECLARE @SniffAvoidingLocalParameter int
    SET @SniffAvoidingLocalParameter = @SniffedFormalParameter

    --Work w/ @SniffAvoidingLocalParameter in sproc body 
    -- ...
于 2008-10-19T00:38:02.127 回答
5

参数嗅探是 SQL Server 用于优化存储过程的查询执行计划的一种技术。首次调用存储过程时,SQL Server 会查看调用的给定参数值,并根据参数值决定使用哪些索引。

所以当第一次调用包含不是很典型的参数时,SQL Server 可能会针对存储过程的后续调用选择并存储一个次优的执行计划。

您可以通过以下任一方式解决此问题

  • 使用WITH RECOMPILE
  • 将参数值复制到存储过程中的局部变量并在查询中使用局部变量。

我什至听说最好不要使用存储过程,而是将查询直接发送到服务器。我最近遇到了同样的问题,我还没有真正的解决方案。对于某些查询,复制到本地变量有助于回到正确的执行计划,对于某些查询,本地变量会降低性能。

我仍然需要对 SQL Server 如何缓存和重用(次优)执行计划进行更多研究。

于 2008-10-22T09:27:19.847 回答
5

根据我的经验,参数嗅探的最佳解决方案是“动态 SQL”。需要注意的两个重要事项是 1. 您应该在动态 sql 查询中使用参数 2. 您应该使用 sp_executesql(而不是 sp_execute),它保存了每个参数值的执行计划

于 2010-11-09T00:25:41.063 回答
0

我有类似的问题。我的存储过程的执行计划需要 30-40 秒。我尝试在查询窗口中使用 SP 语句,执行相同的语句需要几毫秒。然后我制定了在存储过程中声明局部变量并将参数的值传递给局部变量。这使得 SP 执行速度非常快,现在相同的 SP 在几毫秒内执行,而不是 30-40 秒。

于 2013-03-26T12:55:41.597 回答
-1

非常简单和排序,查询优化器使用旧的查询计划来进行频繁运行的查询。但实际上数据的大小也在增加,所以当时需要新的优化计划,并且仍然使用旧的查询计划进行查询优化器。这称为参数嗅探。我还为此创建了详细的帖子。请访问此网址: http ://www.dbrnd.com/2015/05/sql-server-parameter-sniffing/

于 2015-08-21T19:57:41.410 回答
-2

将存储过程更改为批量执行应该会提高速度。

批处理文件选择即:

exec ('select * from order where  order id ='''+ @ordersID')

而不是普通的存储过程选择:

select * from order where  order id = @ordersID

只需传入参数 as nvarchar,您应该会得到更快的结果。

于 2012-07-17T14:12:53.930 回答