为什么Sql Server优化器与参数混淆了?

时间:2009-01-05 20:09:50

标签: sql sql-server optimization

我知道这与参数嗅探有关,但我只是对以下示例中的某些事情感到困惑,即使是一项能够完成许多复杂事情的技术,也是如此。

我们中的许多人遇到了间歇性运行比平时慢几个数量级的存储过程,然后如果从过程中复制出sql并在单独的查询窗口中使用相同的参数值,它会运行得很快像往常一样。

我刚刚修改了这样的程序:

alter procedure p_MyProc
(
    @param1 int
) as -- do a complex query with @param1

到此:

alter procedure p_MyProc
(
    @param1 int
)
as

declare @param1Copy int;
set @param1Copy = @param1;

-- Do the query using @param1Copy

它从跑步一分钟后回到不到一秒钟,就像通常跑步一样。这种行为似乎完全随机。对于10个@param1输入中的9个,查询速度很快,无论它最终需要处理多少数据,或者结果设置多大。但是对于那10个中的1个,它只是迷失了。修复是用查询中的相同int替换int吗?

没用。

[编辑]

@gbn链接到这个问题,详细说明了类似的问题:

Known issue?: SQL Server 2005 stored procedure fails to complete with a parameter

我犹豫地喊“虫子!”因为这经常是一个警察,但这对我来说确实看起来像个错误。当我使用相同的输入运行我的存储过程的两个版本时,我看到相同的查询计划。唯一的区别是原始版本需要运行超过一分钟,而且带有goofy参数复制的版本会立即运行。

9 个答案:

答案 0 :(得分:5)

10中的1/10给出了缓存的错误计划。

RECOMPILE增加了开销,屏蔽允许根据它自己的优点(非常简单)评估每个参数。

如果错误的计划,如果10中的1对索引1产生扫描但是其他9对索引2产生搜索怎么办?例如,10比1是,例如,行的50%?

编辑:其他问题

编辑2:

重新编译不起作用,因为参数在编译时被嗅探 来自其他链接(粘贴):

article解释了......

...parameter values are sniffed during compilation or recompilation...

最后(编辑3):

参数嗅探在当时可能是一个好主意,并且可能在大多数情况下运作良好。我们全面地使用它来获取最终在WHERE子句中的任何参数。 我们不需要使用它,因为我们知道只有少数(更复杂的例如报告或许多参数)可能导致问题,但我们使用它来保持一致性。

当用户抱怨并且我们应该使用屏蔽时,它会回来并咬我们......

答案 1 :(得分:2)

我将代码从测试服务器转移到生产代码时反复遇到此问题 - on two different builds of SQL Server 2005。我认为在SQL Server 2005的某些版本中参数嗅探存在一些大问题。我从未在开发服务器或两个本地开发人员版本框上遇到此问题。我从来没有看到它在SQL Server 2000或任何版本回到6.5上都是一个大问题。

我发现它的情况,唯一的解决方法是使用参数屏蔽,我仍然希望DBA将生产服务器修补到SP3,这样它可能会消失。不起作用的事情:

  • 在EXEC或SP本身中使用WITH RECOMPILE提示。
  • 删除并重新创建SP
  • 使用sp_recompile

请注意,在我正在处理的情况下,自早期调用以来数据没有改变 - 我只是将代码编写到已经加载了数据的生产框上。自SP出现之前,所有调用都没有对数据进行任何更改。

哦,如果SQL Server无法在没有屏蔽的情况下处理这个问题,他们需要添加参数修饰符NOSNIFF等。如果你屏蔽了所有参数会发生什么,所以你有@Something_parm和@Something_var而有人改变代码使用错误的一个,突然之间你再次有一个嗅探问题?另外,您正在污染SP中的命名空间。所有这些我正在“修理”的SP让我疯狂,因为我知道他们将成为经验较少的satff的维护噩梦我将把这个项目交给一天。

答案 2 :(得分:1)

这可能是由于SQL Server为它们编译存储过程和缓存执行计划这一事实,并且缓存的执行计划可能不适合这一新的参数集。您可以尝试使用WITH RECOMPILE选项查看是否是原因。

EXECUTE MyProcedure [parameters] WITH RECOMPILE

WITH RECOMPILE选项将强制SQL Server忽略缓存的计划。

答案 3 :(得分:1)

你可以检查一下SQL Profiler在快速和慢速时读取和执行的时间吗?它可能与根据参数值提取的行数有关。这听起来不像缓存计划问题。

答案 4 :(得分:0)

如上所述,这是一个汇编问题。如果您还原该过程,是否仍会出现此问题?如果再次发生以强制重新编译,您可以尝试使用:

sp_recompile [@objname =]'object'

关于@objname参数的直接来自BOL:

当前数据库中存储过程,触发器,表或视图的限定名称或非限定名称。 object是nvarchar(776),没有默认值。如果object是存储过程或触发器的名称,则下次运行时将重新编译存储过程或触发器。如果object是表或视图的名称,那么引用该表或视图的所有存储过程将在下次运行时重新编译。

如果删除并重新创建该过程,则可能导致客户端尝试执行该过程时失败。您还需要重新应用安全设置。

答案 5 :(得分:0)

提供的参数值是否有可能不是int?

答案 6 :(得分:0)

这是计划缓存的一个问题,它并不总是与您的方案中的参数相关。

(参数嗅探问题是在第一次运行时使用异常参数调用proc时发生的,因此缓存的计划对于那些奇数值很有效,但是对于大多数其他时间调用proc都会很糟糕。)

当应用团队从生产服务器上的高度使用的日志表中删除所有旧记录时,我们遇到了类似的情况。删除记录可以提高性能,对吧?不,表现立刻昙花一现。

事实证明,当表几乎为空时,一个经常使用的存储过程被重新编译,并且它缓存了一个极差的执行计划(“嘿,这里只有50条记录,不妨做一次表扫描!”) 。无论初始参数是什么都会发生。

我们的解决方法是使用sp_recompile强制重新编译。

答案 7 :(得分:0)

每个查询是否引用参数,将其与int值进行比较,没有函数且没有强制转换?

您是否可以使用该参数增加任何表达式的特异性,以更有可能使用多字段索引?

答案 8 :(得分:0)

我知道这是一个2岁的线程,但它可能有助于某人下线。

一旦分析了查询执行计划并知道两个计划之间的区别(查询本身和查询在存储过程中执行的计划有缺陷),您可以使用查询提示修改存储过程中的查询解决问题。这适用于在存储过程中执行时查询使用不正确索引的情况。您可以在表格的适当位置添加以下内容:

SELECT col1, col2, col3
FROM YourTableHere WITH (INDEX (PK_YourIndexHere))

这将强制查询计划使用应该解决问题的正确索引。这并不能解决为什么会发生这种情况,但它确实提供了解决问题的方法,而不必担心复制参数以避免参数嗅探。