Sql server SQL Server中的参数嗅探(或欺骗)

Sql server SQL Server中的参数嗅探(或欺骗),sql-server,tsql,sql-server-2005,parameter-spoofing,Sql Server,Tsql,Sql Server 2005,Parameter Spoofing,不久前,我有一个查询,我为我的一个用户运行了很多次。它仍在进化和调整中,但最终它稳定下来并运行得相当快,所以我们用它创建了一个存储过程 到目前为止,一切正常 不过,存储过程非常慢。查询和进程之间没有实质性差异,但速度变化很大 [背景,我们正在运行SQL Server 2005。] 一位友好的本地DBA(不再在这里工作)看了一眼存储过程,说“参数欺骗!”(编辑:,尽管它可能也被称为“参数嗅探”,这可能解释了我试图搜索时谷歌点击率低的原因。) 我们将一些存储过程抽象为第二个存储过程,将对这个新的内部

不久前,我有一个查询,我为我的一个用户运行了很多次。它仍在进化和调整中,但最终它稳定下来并运行得相当快,所以我们用它创建了一个存储过程

到目前为止,一切正常

不过,存储过程非常慢。查询和进程之间没有实质性差异,但速度变化很大

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

一位友好的本地DBA(不再在这里工作)看了一眼存储过程,说“参数欺骗!”(编辑:,尽管它可能也被称为“参数嗅探”,这可能解释了我试图搜索时谷歌点击率低的原因。)

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

那么,有什么好处呢?有人能解释参数欺骗吗

奖励信用

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

是的,我认为您指的是参数嗅探,这是SQL Server优化器用来尝试计算参数值/范围的一种技术,以便为您的查询选择最佳执行计划。在某些情况下,SQLServer在参数嗅探方面做得很差&没有为查询选择最佳执行计划

我相信这篇博客文章有很好的解释

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


您也可以在原始存储过程中使用with recompile,或在参数中使用optimize for选项。

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

CREATE PROCEDURE uspParameterSniffingAvoidance
    @SniffedFormalParameter int
AS
BEGIN

    DECLARE @SniffAvoidingLocalParameter int
    SET @SniffAvoidingLocalParameter = @SniffedFormalParameter

    --Work w/ @SniffAvoidingLocalParameter in sproc body 
    -- ...

仅供参考-在使用SQL 2005和带有参数的存储过程时,您需要注意其他事项

SQL Server将使用使用的第一个参数编译存储过程的执行计划。因此,如果您运行以下命令:

usp_QueryMyDataByState 'Rhode Island'
执行计划在处理小州的数据时效果最好。但如果有人转身逃跑:

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

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


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

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

因此,当第一个调用不包含非常典型的参数时,SQLServer可能会针对存储过程的以下调用选择并存储次优执行计划

你可以通过以下两种方法来解决这个问题

  • 在重新编译时使用
  • 将参数值复制到存储过程中的局部变量,并在查询中使用局部变量
我甚至听说最好不要使用存储过程,而是直接将查询发送到服务器。 我最近遇到了同样的问题,我还没有真正的解决办法。 对于某些查询,复制到本地变量有助于返回正确的执行计划,对于某些查询,本地变量会降低性能


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

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

更改存储过程以成批执行应该会提高速度

批处理文件选择,即:

exec ('select * from order where  order id ='''+ @ordersID')
选择以下选项,而不是常规存储过程:

select * from order where  order id = @ordersID

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

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

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

+1,但请注意