参数嗅探(或欺骗)在SQL Server中
前一段时间,我有一个查询,我为我的一个用户跑了很多。 它还在进化和调整,但最终稳定和运行相当快,所以我们创build了一个存储过程。
到目前为止,如此正常。
存储过程,但是,狗慢。 查询和过程没有实质性区别,但速度变化很大。
[背景,我们正在运行SQL Server 2005.]
一个友好的本地DBA(不再在这里工作)看了一下存储过程,并说“参数欺骗! ( 编辑:虽然它似乎也可能被称为“参数嗅探”,这可能解释了当我试图search出谷歌命中的缺乏)。
我们将一些存储过程抽象为第二个存储过程,将这个新的内部过程调用包装到预先存在的外部过程中,称为外部过程,并且嘿,它和原始查询一样快。
那么,是什么给了? 有人可以解释参数欺骗?
红利信贷
- 强调如何避免它
- build议如何识别可能的原因
- 讨论替代策略,例如统计数据,指数,关键字,以缓解这种情况
仅供参考 – 当您使用SQL 2005并使用参数存储特效时,您需要了解其他信息。
SQL Server将使用第一个参数编译存储过程的执行计划。 所以如果你运行这个:
usp_QueryMyDataByState 'Rhode Island'
执行计划将与小国的数据最好地工作。 但是如果有人转身跑步:
usp_QueryMyDataByState 'Texas'
为德克萨斯州规模的数据devise的执行计划可能没有效率。 这会在服务器重启时产生令人惊讶的结果,因为新生成的执行计划将针对首先使用的任何参数 – 不一定是最好的参数。 这个计划不会被重新编译,直到有一个很重要的原因,比如重build统计数据。
这就是查询计划的出处,SQL Server 2008提供了许多新function,可以帮助DBA长期定位特定的查询计划,无论哪个参数先被调用。
我担心的是,当你重build你的存储过程时,你强制执行计划重新编译。 你用你最喜欢的参数调用它,当然这很快 – 但问题可能不是存储过程。 可能是因为存储过程在某个时刻被重新编译了一些不寻常的参数,从而导致了一个效率低下的查询计划。 您可能没有解决任何问题,下次重新启动服务器或重新编译查询计划时,可能会遇到同样的问题。
是的,我认为你的意思是参数嗅探,这是SQL Server优化器用来试图找出参数值/范围的技术,因此它可以为您的查询select最佳的执行计划。 在某些情况下,SQL Server在参数嗅探方面做得不好,并且不会为查询select最佳的执行计划。
我相信这篇博客文章http://blogs.msdn.com/queryoptteam/archive/2006/03/31/565991.aspx有很好的解释。;
看起来你的例子中的DBAselect了选项#4来将查询移动到另一个存储过程到单独的过程上下文。
你也可以在原始的sproc上用重新编译或者在参数上使用优化选项。
加速这个过程的一个简单方法就是将input参数重新分配到存储区一开始的局部参数,例如
CREATE PROCEDURE uspParameterSniffingAvoidance @SniffedFormalParameter int AS BEGIN DECLARE @SniffAvoidingLocalParameter int SET @SniffAvoidingLocalParameter = @SniffedFormalParameter --Work w/ @SniffAvoidingLocalParameter in sproc body -- ...
根据我的经验,参数嗅探的最佳解决scheme是“dynamicSQL”。 需要注意的两个重要的事情是:1.您应该在dynamicsql查询中使用参数2.您应该使用sp_executesql(而不是sp_execute),这会为每个参数值保存执行计划
参数嗅探是SQL Server用来优化存储过程的查询执行计划的技术。 当您第一次调用存储过程时,SQL Server会查看您调用的给定参数值,并根据参数值决定使用哪个索引。
所以当第一次调用包含非常典型的参数时,SQL Server可能会针对存储过程的以下调用select并存储一个次优执行计划。
你可以通过任何方式解决这个问题
- 使用
WITH RECOMPILE
- 将参数值复制到存储过程中的局部variables,并在查询中使用本地variables。
我甚至听说最好不要使用存储过程,而是直接将查询发送到服务器。 我最近遇到了同样的问题,我还没有真正的解决scheme。 对于一些查询,复制到本地variables有助于回到正确的执行计划,对于一些查询性能降低本地variables。
我仍然需要做更多的研究,了解SQL Server如何caching和重用(次优)执行计划。
我有类似的问题。 我的存储过程的执行计划花了30-40秒。 我试图在查询窗口中使用SP语句,并且执行相同的操作花了很less的时间。 然后我在存储过程中声明了局部variables,并将参数的值传递给局部variables。 这使得SP执行速度非常快,现在相同的SP在几毫秒内而不是30-40秒内执行。
查询优化器使用旧查询计划进行频繁运行的查询非常简单。 但实际上数据的大小也在增加,所以当时新的优化计划是需要的,而且还是使用旧的查询计划查询优化器。 这被称为参数嗅探。 我也创build了这个详细的post。 请访问以下url: http : //www.dbrnd.com/2015/05/sql-server-parameter-sniffing/
将您的存储过程更改为批量执行应该会提高速度。
batch fileselect即:
exec ('select * from order where order id ='''+ @ordersID')
而不是正常的存储过程select:
select * from order where order id = @ordersID
只要传入参数nvarchar
,你应该得到更快的结果。